Documentation
¶
Overview ¶
Package logger provides structured logging with context extraction and Sentry integration.
This package extends the standard library's log/slog with two key capabilities: automatic context-based attribute injection and optional Sentry error reporting. It is designed for production applications that need consistent, enriched logs with minimal boilerplate.
Overview ¶
The package provides:
- Context extractors that automatically inject request-scoped values (e.g., request IDs, user IDs)
- A decorator pattern that wraps any slog.Handler to add extraction behavior
- Sentry integration for error tracking with graceful fallback when unconfigured
- Multi-handler support for routing logs to multiple destinations
Basic Usage ¶
Create a logger with context extractors:
// Define an extractor for request ID
requestIDExtractor := func(ctx context.Context) (slog.Attr, bool) {
if reqID, ok := ctx.Value("request_id").(string); ok && reqID != "" {
return slog.String("request_id", reqID), true
}
return slog.Attr{}, false
}
// Create logger with extractors
log := logger.New(requestIDExtractor)
// Use with context - request_id is automatically included
ctx := context.WithValue(context.Background(), "request_id", "abc-123")
log.InfoContext(ctx, "request processed", slog.Int("status", 200))
// Output: {"level":"INFO","msg":"request processed","status":200,"request_id":"abc-123"}
Sentry Integration ¶
For production error tracking, use NewWithSentry:
cfg := logger.SentryConfig{
DSN: os.Getenv("SENTRY_DSN"),
Environment: "production",
MinLevel: slog.LevelWarn, // Send warnings and errors to Sentry
}
log := logger.NewWithSentry(cfg, requestIDExtractor)
// Errors create Issues in Sentry, warnings are stored for context
log.ErrorContext(ctx, "payment failed", slog.String("user_id", "user-456"))
If SENTRY_DSN is empty, the logger gracefully falls back to stdout-only logging, making it safe to use the same code path in development and production.
Context Extractors ¶
A ContextExtractor is a function that extracts a log attribute from context:
type ContextExtractor func(ctx context.Context) (slog.Attr, bool)
Extractors are called on every log call, ensuring fresh values for request-scoped data. Return false from the extractor to skip adding the attribute for that log entry.
Common extractors include:
- Request ID extractor for HTTP request tracing
- User ID extractor for authentication context
- Tenant ID extractor for multi-tenant applications
Handler Decoration ¶
The LogHandlerDecorator can wrap any slog.Handler to add context extraction:
// Wrap a custom handler
jsonHandler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})
decorated := logger.NewLogHandlerDecorator(jsonHandler, extractors...)
log := slog.New(decorated)
This allows using context extractors with any handler implementation.
Architecture ¶
The package uses several design patterns:
Decorator Pattern: LogHandlerDecorator wraps any slog.Handler, intercepting Handle calls to inject extracted attributes before delegating to the underlying handler.
Multi-Handler Pattern: An internal multiHandler forwards logs to multiple destinations, enabling simultaneous stdout and Sentry logging.
Graceful Degradation: Sentry integration fails gracefully - if DSN is missing or initialization fails, logging continues to stdout without disruption.
Index ¶
- func New(extractors ...ContextExtractor) *slog.Logger
- func NewLogHandlerDecorator(next slog.Handler, extractors ...ContextExtractor) slog.Handler
- func NewNope() *slog.Logger
- func NewWithSentry(cfg SentryConfig, extractors ...ContextExtractor) *slog.Logger
- type ContextExtractor
- type LogHandlerDecorator
- func (h *LogHandlerDecorator) Enabled(ctx context.Context, level slog.Level) bool
- func (h *LogHandlerDecorator) Handle(ctx context.Context, rec slog.Record) error
- func (h *LogHandlerDecorator) WithAttrs(attrs []slog.Attr) slog.Handler
- func (h *LogHandlerDecorator) WithGroup(name string) slog.Handler
- type SentryConfig
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func New ¶
func New(extractors ...ContextExtractor) *slog.Logger
New creates a JSON-formatted logger with optional context extractors.
func NewLogHandlerDecorator ¶
func NewLogHandlerDecorator(next slog.Handler, extractors ...ContextExtractor) slog.Handler
NewLogHandlerDecorator creates a new decorated handler with context extractors. Filters nil extractors to prevent runtime panics from misconfigured options.
func NewNope ¶
NewNope creates a no-op logger that discards all output. Use this as a default when logging is not configured.
func NewWithSentry ¶
func NewWithSentry(cfg SentryConfig, extractors ...ContextExtractor) *slog.Logger
NewWithSentry creates a logger that sends logs to both stdout and Sentry. If DSN is empty, only stdout logging is enabled (graceful fallback for local dev). Context extractors are applied to logs sent to both destinations.
Types ¶
type ContextExtractor ¶
ContextExtractor extracts a slog attribute from context.
type LogHandlerDecorator ¶
type LogHandlerDecorator struct {
// contains filtered or unexported fields
}
LogHandlerDecorator wraps a slog.Handler and injects context-extracted attributes during logging. Extraction occurs per-log-call to capture fresh request-scoped values (e.g., request IDs).
func (*LogHandlerDecorator) Handle ¶
Handle extracts context attributes and delegates to the underlying handler.
type SentryConfig ¶
type SentryConfig struct {
DSN string `env:"DSN"`
Environment string `env:"ENVIRONMENT" envDefault:"production"`
MinLevel string `env:"MIN_LEVEL" envDefault:"warn"`
}
SentryConfig holds Sentry integration configuration.