Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions v2/callctx/callctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ package callctx
import (
"context"
"fmt"
"log/slog"
)

const (
Expand Down Expand Up @@ -125,3 +126,21 @@ func TelemetryFromContext(ctx context.Context, key string) (string, bool) {
val, ok := ctx.Value(telemetryKey(key)).(string)
return val, ok
}

// loggerKey is a private type used to store/retrieve the logger context value.
type loggerContextKey string

const loggerCKey = loggerContextKey("logger")

// WithLogger injects a slog.Logger into the context. This logger will be
// extracted by the client library or transport wrappers to emit logs.
func WithLogger(ctx context.Context, logger *slog.Logger) context.Context {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe rename to WithLoggerContext to match WithTelemetryContext?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return context.WithValue(ctx, loggerCKey, logger)
}

// LoggerFromContext extracts a slog.Logger from the context.
// The returned bool indicates whether a logger was found.
func LoggerFromContext(ctx context.Context) (*slog.Logger, bool) {
logger, ok := ctx.Value(loggerCKey).(*slog.Logger)
return logger, ok
}
22 changes: 22 additions & 0 deletions v2/callctx/callctx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
package callctx

import (
"bytes"
"context"
"log/slog"
"sync"
"testing"

Expand Down Expand Up @@ -122,3 +124,23 @@ func TestSetHeaders_race(t *testing.T) {
}
wg.Wait()
}

func TestLoggerFromContext(t *testing.T) {
ctx := context.Background()

// Should not find a logger in an empty context
l, ok := LoggerFromContext(ctx)
if ok || l != nil {
t.Errorf("LoggerFromContext(ctx) = %v, %v; want nil, false", l, ok)
}

// Should extract the exact logger that was injected
var logOutput bytes.Buffer
injectedLogger := slog.New(slog.NewTextHandler(&logOutput, nil))
ctx = WithLogger(ctx, injectedLogger)

extractedLogger, ok := LoggerFromContext(ctx)
if !ok || extractedLogger != injectedLogger {
t.Errorf("LoggerFromContext(ctx) = %v, %v; want %v, true", extractedLogger, ok, injectedLogger)
}
}