Skip to content

Commit

Permalink
refactor: re-write log arg enrichment
Browse files Browse the repository at this point in the history
  • Loading branch information
leg100 committed Sep 13, 2024
1 parent 2415416 commit 5eab6fb
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 145 deletions.
2 changes: 1 addition & 1 deletion internal/logging/discard.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ func (noop) Warn(msg string, args ...any) {}

func (noop) Error(msg string, args ...any) {}

func (noop) AddEnricher(enricher Enricher) {}
func (noop) AddArgsUpdater(updater ArgsUpdater) {}
78 changes: 69 additions & 9 deletions internal/logging/enricher.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,83 @@
package logging

import (
"reflect"

"github.com/leg100/pug/internal/resource"
)

// enricher enriches a log record with further meaningful attributes that aren't
// readily available to the caller.
type enricher struct {
enrichers []Enricher
updaters []ArgsUpdater
}

// Enricher implementations update a log record with further info
type Enricher interface {
EnrichLogRecord(args ...any) []any
func (e *enricher) AddArgsUpdater(updater ArgsUpdater) {
e.updaters = append(e.updaters, updater)
}

func (e *enricher) AddEnricher(enricher Enricher) {
e.enrichers = append(e.enrichers, enricher)
func (e *enricher) enrich(args ...any) []any {
for _, en := range e.updaters {
args = en.UpdateArgs(args...)
}
return args
}

func (e *enricher) enrich(args ...any) []any {
for _, en := range e.enrichers {
args = en.EnrichLogRecord(args...)
// ArgsUpdater updates a log message's arguments.
type ArgsUpdater interface {
UpdateArgs(args ...any) []any
}

// ReferenceUpdater checks log arguments for references to T via its ID, either directly
// or via a struct field, and updates or adds T to the log arguments
// accordingly.
type ReferenceUpdater[T any] struct {
Getter[T]

Name string
Field string
}

type Getter[T any] interface {
Get(resource.ID) (T, error)
}

func (e *ReferenceUpdater[T]) UpdateArgs(args ...any) []any {
for i, arg := range args {
// Where an argument is of type resource.ID, try and retrieve the
// resource corresponding to the ID and replace argument with the
// resource.
if id, ok := arg.(resource.ID); ok {
t, err := e.Get(id)
if err != nil {
continue
}
// replace id with T
args[i] = t
return args
}
// Where an argument is a struct (or a pointer to a struct), check if it
// has a field matching the expect field name, with a corresponding
// value of type resource.ID, and if so, try and retrieve resource with
// that resource.ID and add it as a log argument preceded with e.Name.
v := reflect.Indirect(reflect.ValueOf(arg))
if v.Kind() != reflect.Struct {
continue
}
f := reflect.Indirect(v.FieldByName(e.Field))
if !f.IsValid() {
continue
}
id, ok := f.Interface().(resource.ID)
if !ok {
continue
}
t, err := e.Get(id)
if err != nil {
// T with id does not exist
continue
}
return append(args, e.Name, t)
}
return args
}
72 changes: 72 additions & 0 deletions internal/logging/enricher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package logging

import (
"testing"

"github.com/leg100/pug/internal/resource"
"github.com/stretchr/testify/assert"
)

func TestReferenceUpdater(t *testing.T) {
res := &fakeResource{ID: resource.NewID(resource.Module)}
updater := &ReferenceUpdater[*fakeResource]{
Getter: &fakeResourceGetter{res: res},
Name: "fake",
Field: "FakeResourceID",
}

t.Run("replace resource id with resource", func(t *testing.T) {
args := []any{"fake", res.ID}
got := updater.UpdateArgs(args...)

want := []any{"fake", res}
assert.Equal(t, want, got)
})

t.Run("add resource when referenced from struct with pointer field", func(t *testing.T) {
type logMsgArg struct {
FakeResourceID *resource.ID
}

args := []any{"arg1", logMsgArg{FakeResourceID: &res.ID}}
got := updater.UpdateArgs(args...)

want := append(args, "fake", res)
assert.Equal(t, want, got)
})

t.Run("add resource when referenced from struct with non-pointer field", func(t *testing.T) {
type logMsgArg struct {
FakeResourceID resource.ID
}

args := []any{"arg1", logMsgArg{FakeResourceID: res.ID}}
got := updater.UpdateArgs(args...)

want := append(args, "fake", res)
assert.Equal(t, want, got)
})

t.Run("handle nil pointer from struct", func(t *testing.T) {
type logMsgArg struct {
FakeResourceID *resource.ID
}

args := []any{"arg1", logMsgArg{FakeResourceID: nil}}
got := updater.UpdateArgs(args...)

assert.Equal(t, got, got)
})
}

type fakeResource struct {
resource.ID
}

type fakeResourceGetter struct {
res *fakeResource
}

func (f *fakeResourceGetter) Get(resource.ID) (*fakeResource, error) {
return f.res, nil
}
2 changes: 1 addition & 1 deletion internal/logging/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ type Interface interface {
Info(msg string, args ...any)
Warn(msg string, args ...any)
Error(msg string, args ...any)
AddEnricher(enricher Enricher)
AddArgsUpdater(enricher ArgsUpdater)
}
66 changes: 0 additions & 66 deletions internal/module/log_enricher.go

This file was deleted.

6 changes: 5 additions & 1 deletion internal/module/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ func NewService(opts ServiceOptions) *Service {
broker := pubsub.NewBroker[*Module](opts.Logger)
table := resource.NewTable(broker)

opts.Logger.AddEnricher(&logEnricher{table: table})
opts.Logger.AddArgsUpdater(&logging.ReferenceUpdater[*Module]{
Getter: table,
Name: "module",
Field: "ModuleID",
})

return &Service{
table: table,
Expand Down
66 changes: 0 additions & 66 deletions internal/workspace/log_enricher.go

This file was deleted.

6 changes: 5 additions & 1 deletion internal/workspace/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ func NewService(opts ServiceOptions) *Service {
broker := pubsub.NewBroker[*Workspace](opts.Logger)
table := resource.NewTable(broker)

opts.Logger.AddEnricher(&logEnricher{table: table})
opts.Logger.AddArgsUpdater(&logging.ReferenceUpdater[*Workspace]{
Getter: table,
Name: "workspace",
Field: "WorkspaceID",
})

s := &Service{
Broker: broker,
Expand Down

0 comments on commit 5eab6fb

Please sign in to comment.