swap from standalone attrs call to slog.LogValuer interface
This commit is contained in:
@@ -9,7 +9,7 @@ Mirror: https://github.com/wisehodl/go-mana-component
|
||||
|
||||
- Injects a named component identity into a `context.Context` at library boundaries
|
||||
- Propagates module identity and component hierarchy across layers
|
||||
- Provides `slog` attributes for structured logging and a string map for generic consumers
|
||||
- Implements `slog.LogValuer` for use as a structured log attribute, and provides a string map for generic consumers
|
||||
|
||||
## What this library does not do
|
||||
|
||||
@@ -40,15 +40,15 @@ constructs a logger internally.
|
||||
### At a library boundary
|
||||
|
||||
A top-level constructor receives a context and creates a new component
|
||||
identity. Injecting the component attributes on the logger allows it to carry
|
||||
`module` and `path` automatically.
|
||||
identity. Passing the component as a `slog.Any` group attribute allows the
|
||||
logger to carry `module` and `path` automatically.
|
||||
|
||||
```go
|
||||
func NewPool(ctx context.Context, id string, handler slog.Handler) (*Pool, error) {
|
||||
ctx = component.MustNew(ctx, "honeybee", "outbound_pool")
|
||||
|
||||
attrs, _ := component.Attrs(ctx)
|
||||
logger := slog.New(handler).WithAttrs(attrs).With(slog.String("pool_id", id))
|
||||
c, _ := component.Get(ctx)
|
||||
logger := slog.New(handler).With(slog.Any("component", c), slog.String("pool_id", id))
|
||||
|
||||
return &Pool{ctx: ctx, logger: logger}, nil
|
||||
}
|
||||
@@ -63,8 +63,8 @@ path. No parent identifiers need to be passed as arguments.
|
||||
func NewWorker(ctx context.Context, id string, handler slog.Handler) (*Worker, error) {
|
||||
ctx = component.MustExtend(ctx, "outbound_worker")
|
||||
|
||||
attrs, _ := component.Attrs(ctx)
|
||||
logger := slog.New(handler).WithAttrs(attrs).With(slog.Any("peer_id", id))
|
||||
c, _ := component.Get(ctx)
|
||||
logger := slog.New(handler).With(slog.Any("component", c), slog.Any("peer_id", id))
|
||||
|
||||
return &Worker{ctx: ctx, logger: logger}, nil
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ type Component interface {
|
||||
Module() string
|
||||
Path() []string
|
||||
PathString() string
|
||||
LogValue() slog.Value
|
||||
}
|
||||
|
||||
type component struct {
|
||||
@@ -27,6 +28,12 @@ type component struct {
|
||||
func (c component) Module() string { return c.module }
|
||||
func (c component) Path() []string { return slices.Clone(c.path) }
|
||||
func (c component) PathString() string { return strings.Join(c.path, ".") }
|
||||
func (c component) LogValue() slog.Value {
|
||||
return slog.GroupValue(
|
||||
slog.String("module", c.module),
|
||||
slog.String("path", c.PathString()),
|
||||
)
|
||||
}
|
||||
|
||||
func insert(ctx context.Context, module string, name string, path []string) context.Context {
|
||||
return context.WithValue(ctx, storageKey, component{
|
||||
@@ -117,16 +124,3 @@ func GetFields(ctx context.Context) (map[string]string, bool) {
|
||||
"path": c.PathString(),
|
||||
}, true
|
||||
}
|
||||
|
||||
// Attrs returns the slog projection of GetFields.
|
||||
func Attrs(ctx context.Context) ([]slog.Attr, bool) {
|
||||
fields, ok := GetFields(ctx)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return []slog.Attr{
|
||||
slog.String("module", fields["module"]),
|
||||
slog.String("path", fields["path"]),
|
||||
}, true
|
||||
}
|
||||
|
||||
+12
-17
@@ -2,7 +2,6 @@ package component
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"slices"
|
||||
"testing"
|
||||
)
|
||||
@@ -120,22 +119,18 @@ func TestGetFields(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func assertAttr(t *testing.T, attr slog.Attr, key string, value string) {
|
||||
t.Helper()
|
||||
if attr.Key != key {
|
||||
t.Errorf("expected attr key %q, got %q", key, attr.Key)
|
||||
}
|
||||
if attr.Value.String() != value {
|
||||
t.Errorf("expected attr value %q, got %q", value, attr.Value.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttrs(t *testing.T) {
|
||||
func TestLogValue(t *testing.T) {
|
||||
ctx := MustNew(context.Background(), "mymodule", "mycomponent")
|
||||
attrs, ok := Attrs(ctx)
|
||||
if !ok {
|
||||
t.Fatal("expected attrs in context")
|
||||
c, _ := Get(ctx)
|
||||
v := c.LogValue()
|
||||
attrs := v.Group()
|
||||
if len(attrs) != 2 {
|
||||
t.Fatalf("expected 2 attrs, got %d", len(attrs))
|
||||
}
|
||||
if attrs[0].Key != "module" || attrs[0].Value.String() != "mymodule" {
|
||||
t.Errorf("expected module=mymodule, got %s=%s", attrs[0].Key, attrs[0].Value.String())
|
||||
}
|
||||
if attrs[1].Key != "path" || attrs[1].Value.String() != "mycomponent" {
|
||||
t.Errorf("expected path=mycomponent, got %s=%s", attrs[1].Key, attrs[1].Value.String())
|
||||
}
|
||||
assertAttr(t, attrs[0], "module", "mymodule")
|
||||
assertAttr(t, attrs[1], "path", "mycomponent")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user