diff --git a/cmd/root/logger.go b/cmd/root/logger.go index 29e75ed4..b20599c5 100644 --- a/cmd/root/logger.go +++ b/cmd/root/logger.go @@ -21,7 +21,10 @@ func initializeLogger(ctx context.Context, cmd *cobra.Command) (context.Context, opts := slog.HandlerOptions{} opts.Level = logLevel.Level() opts.AddSource = true - opts.ReplaceAttr = log.ReplaceLevelAttr + opts.ReplaceAttr = log.ReplaceAttrFunctions{ + log.ReplaceLevelAttr, + log.ReplaceSourceAttr, + }.ReplaceAttr // Open the underlying log file if the user configured an actual file to log to. err := logFile.Open() diff --git a/libs/log/replace_attr.go b/libs/log/replace_attr.go new file mode 100644 index 00000000..55d2c15f --- /dev/null +++ b/libs/log/replace_attr.go @@ -0,0 +1,18 @@ +package log + +import "golang.org/x/exp/slog" + +type ReplaceAttrFunction func(groups []string, a slog.Attr) slog.Attr + +// ReplaceAttrFunctions enables grouping functions that replace attributes +// from a [slog.Handler]. Useful when multiple attributes need replacing. +type ReplaceAttrFunctions []ReplaceAttrFunction + +// ReplaceAttr can be used as a value to pass to a handler to combine +// multiple functions to replace attributes. +func (fns ReplaceAttrFunctions) ReplaceAttr(groups []string, a slog.Attr) slog.Attr { + for _, fn := range fns { + a = fn(groups, a) + } + return a +} diff --git a/libs/log/replace_attr_test.go b/libs/log/replace_attr_test.go new file mode 100644 index 00000000..dce11be1 --- /dev/null +++ b/libs/log/replace_attr_test.go @@ -0,0 +1,32 @@ +package log + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "golang.org/x/exp/slog" +) + +func testReplaceA(groups []string, a slog.Attr) slog.Attr { + if a.Key == "foo" { + return slog.Int("foo", int(a.Value.Int64())+1) + } + return a +} + +func TestReplaceAttrGroup(t *testing.T) { + var foo, bar, out slog.Attr + + fn := ReplaceAttrFunctions{ + testReplaceA, + testReplaceA, + } + + foo = slog.Int("foo", 0) + out = fn.ReplaceAttr([]string{}, foo) + assert.EqualValues(t, 2, out.Value.Int64()) + + bar = slog.Int("bar", 0) + out = fn.ReplaceAttr([]string{}, bar) + assert.EqualValues(t, 0, out.Value.Int64()) +} diff --git a/libs/log/source.go b/libs/log/source.go new file mode 100644 index 00000000..4a30aaab --- /dev/null +++ b/libs/log/source.go @@ -0,0 +1,17 @@ +package log + +import ( + "path/filepath" + + "golang.org/x/exp/slog" +) + +// ReplaceSourceAttr rewrites the source attribute to include only the file's basename. +func ReplaceSourceAttr(groups []string, a slog.Attr) slog.Attr { + if a.Key != slog.SourceKey { + return a + } + + a.Value = slog.StringValue(filepath.Base(a.Value.String())) + return a +} diff --git a/libs/log/source_test.go b/libs/log/source_test.go new file mode 100644 index 00000000..065c6f68 --- /dev/null +++ b/libs/log/source_test.go @@ -0,0 +1,20 @@ +package log + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "golang.org/x/exp/slog" +) + +func TestReplaceSourceAttrSourceKey(t *testing.T) { + attr := slog.String(slog.SourceKey, "bricks/bundle/phases/phase.go:30") + out := ReplaceSourceAttr([]string{}, attr) + assert.Equal(t, "phase.go:30", out.Value.String()) +} + +func TestReplaceSourceAttrOtherKey(t *testing.T) { + attr := slog.String("foo", "bar") + out := ReplaceSourceAttr([]string{}, attr) + assert.Equal(t, attr, out) +}