Skip to content

Commit

Permalink
Wrap context-aware handlers + values around loggers by default.
Browse files Browse the repository at this point in the history
Previously, we didn't allowed you to provide any handler you wanted,
which didn't quite meet expectations for using clog.
This changes the behavior to always wrap the logger with a context
handler.

Additionally, when a new logger is created any values present in the
context are added to the logger, which should allow the values to be
preserved even if the default slog funcs are used.
  • Loading branch information
wlynch committed Jan 23, 2025
1 parent 56a6d5a commit fb1c34d
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 5 deletions.
14 changes: 9 additions & 5 deletions logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type Logger struct {

// DefaultLogger returns a new logger that uses the default [slog.Logger].
func DefaultLogger() *Logger {
return NewLogger(slog.Default())
return NewLogger(nil)
}

// NewLogger returns a new logger that wraps the given [slog.Logger] with the default context.
Expand All @@ -28,7 +28,11 @@ func NewLogger(l *slog.Logger) *Logger {
// NewLoggerWithContext returns a new logger that wraps the given [slog.Logger].
func NewLoggerWithContext(ctx context.Context, l *slog.Logger) *Logger {
if l == nil {
l = slog.Default()
l = slog.New(NewHandler(slog.Default().Handler()))
}
// Attach any existing context values onto the logger.
for k, v := range get(ctx) {
l = l.With(k, v)
}
return &Logger{
ctx: ctx,
Expand All @@ -38,12 +42,12 @@ func NewLoggerWithContext(ctx context.Context, l *slog.Logger) *Logger {

// New returns a new logger that wraps the given [slog.Handler].
func New(h slog.Handler) *Logger {
return NewLogger(slog.New(h))
return NewLogger(slog.New(NewHandler(h)))
}

// New returns a new logger that wraps the given [slog.Handler].
func NewWithContext(ctx context.Context, h slog.Handler) *Logger {
return NewLoggerWithContext(ctx, slog.New(h))
return NewLoggerWithContext(ctx, slog.New(NewHandler(h)))
}

// With calls [Logger.With] on the default logger.
Expand Down Expand Up @@ -180,5 +184,5 @@ func FromContext(ctx context.Context) *Logger {
Logger: logger,
}
}
return NewLoggerWithContext(ctx, slog.Default())
return NewLoggerWithContext(ctx, nil)
}
50 changes: 50 additions & 0 deletions logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,53 @@ func TestWith(t *testing.T) {
t.Errorf("want %v, got %v", want, ctx)
}
}

func TestDefaultHandler(t *testing.T) {
old := slog.Default()
defer func() {
slog.SetDefault(old)
}()

b := new(bytes.Buffer)
slog.SetDefault(slog.New(slog.NewJSONHandler(b, testopts)))

t.Run("Info", func(t *testing.T) {
FromContext(WithValues(context.Background(), "a", "b")).Info("")
want := map[string]any{
"level": "INFO",
"msg": "",
"a": "b",
}
var got map[string]any
if err := json.Unmarshal(b.Bytes(), &got); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(want, got) {
t.Errorf("want %v, got %v", want, got)
}
})

b.Reset()

t.Run("InfoContext", func(t *testing.T) {
// Set logger with original value
ctx := WithValues(context.Background(), "a", "b")
logger := FromContext(ctx)

// Override value in request context - we expect this to overwrite the original value set in the logger
logger.InfoContext(WithValues(ctx, "a", "c"), "")

want := map[string]any{
"level": "INFO",
"msg": "",
"a": "c",
}
var got map[string]any
if err := json.Unmarshal(b.Bytes(), &got); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(want, got) {
t.Errorf("want %v, got %v", want, got)
}
})
}

0 comments on commit fb1c34d

Please sign in to comment.