Context Logger

A lightweight Go library that enhances Zap logger by automatically adding fields from context.Context.
Features
- Seamlessly integrates with Zap logger
- Extracts values from context and adds them as structured log fields
- Supports multiple extractors that can be combined
- Includes built-in extractors for common use cases
- Extensible with custom extractors
- Keeps Zap APIs familiar via a context-aware wrapper
Installation
go get github.com/adlandh/context-logger
Usage
Basic Usage
package main
import (
"context"
ctxlog "github.com/adlandh/context-logger"
"go.uber.org/zap"
)
type contextKey string
func (c contextKey) String() string {
return string(c)
}
var userIDKey = contextKey("user_id")
func main() {
// Create a Zap logger
logger, _ := zap.NewProduction()
// Create a context logger with a value extractor (using New or WithContext)
ctxLogger := ctxlog.New(logger, ctxlog.WithValueExtractor(userIDKey))
// Or equivalently:
// ctxLogger := ctxlog.WithContext(logger, ctxlog.WithValueExtractor(userIDKey))
// Create a context with a value
ctx := context.WithValue(context.Background(), userIDKey, "user-123")
// Log with the context
// This will automatically include "user_id":"user-123" in the log entry
ctxLogger.Ctx(ctx).Info("User action performed")
}
Ctx(ctx) returns a *zap.Logger with fields extracted from ctx attached via With(...); Ctx(nil) is supported and uses context.Background().
Extractors and Composition
WithContext accepts one or more extractors. Each extractor can add fields derived from the context, and all extractors are applied for every log call.
ctxLogger := ctxlog.WithContext(
logger,
ctxlog.WithValueExtractor(userIDKey),
ctxlog.WithValueExtractor(contextKey("request_id")),
ctxlog.WithDeadlineExtractor(),
ctxlog.WithContextCarrier("ctx"),
)
Context Key Guidelines
WithValueExtractor expects keys that implement fmt.Stringer. This lets the extractor use the key's string value as the log field name.
Use a typed key (like the contextKey example) instead of raw string keys to avoid collisions across packages.
type contextKey string
func (c contextKey) String() string { return string(c) }
Web Application Example
See the full example for a web application using Echo framework.
- WithValueExtractor: Extracts values from context using keys that implement
fmt.Stringer
- WithDeadlineExtractor: Extracts deadline metadata from context (
context_deadline_at, context_time_left) and adds context_error when the context is done
- WithContextCarrier: Attaches the
context.Context to the logger for custom cores/encoders (field is not emitted by default)
Usage note: WithContextCarrier is useful when you have a custom zap core/encoder that knows how to pull values from the context. The carrier field is a skip-type field, so it will not appear in logs unless your core/encoder handles it explicitly.
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ctxLogger := ctxlog.WithContext(logger, ctxlog.WithDeadlineExtractor())
ctxLogger.Ctx(ctx).Info("processing request")
// Adds:
// - context_deadline_at (time.Time)
// - context_time_left (time.Duration)
// - context_error (string, only when ctx.Err() is non-nil)
-
Sentry Extractor: Extracts Sentry trace information (trace_id, span_id, span_status, span_op)
import "github.com/adlandh/context-logger/sentry-extractor"
// Use the extractor
ctxLogger := ctxlog.WithContext(logger, sentryextractor.With())
-
OpenTelemetry Extractor: Extracts OpenTelemetry trace information (trace_id, span_id)
import "github.com/adlandh/context-logger/otel-extractor"
// Use the extractor
ctxLogger := ctxlog.WithContext(logger, otelextractor.With())
Each extractor module has its own go.mod, so install them explicitly:
go get github.com/adlandh/context-logger/otel-extractor
go get github.com/adlandh/context-logger/sentry-extractor
You can create custom extractors by implementing the ContextExtractor function type:
func MyCustomExtractor() ctxlog.ContextExtractor {
return func(ctx context.Context) []zap.Field {
// Extract values from context
// Return them as zap.Field slice
return []zap.Field{
zap.String("custom_field", "custom_value"),
}
}
}
API Overview
New(logger, extractors...) creates a new ContextLogger (alias for WithContext)
WithContext(logger, extractors...) wraps a Zap logger and returns a context-aware facade
Ctx(ctx) returns a logger bound to that context for the next call
With(extractors...) returns a new ContextLogger with additional extractors (does not modify original)
Logger() returns the underlying Zap logger
ContextExtractor is func(context.Context) []zap.Field
Testing
go test -cover -race ./...
cd otel-extractor && go test -cover -race ./...
cd sentry-extractor && go test -cover -race ./...