Structured logger backed by log/slog with request-context enrichment, extra-field context helpers, and duck-typed automatic error enrichment. What's included: - `Logger` interface with Debug / Info / Warn / Error / With / WithContext; `New(Options)` constructor writing to os.Stdout - `WithRequestID` / `GetRequestID` and `WithField` / `WithFields` context helpers — package owns both context keys - Automatic error_code and context-field enrichment in Logger.Error via duck-typed errorWithCode / errorWithContext interfaces (no xerrors import) Tested-via: todo-api POC integration Reviewed-against: docs/adr/
44 lines
1.4 KiB
Go
44 lines
1.4 KiB
Go
package logz
|
|
|
|
import "context"
|
|
|
|
// ctxRequestIDKey is the context key for the request correlation ID.
|
|
type ctxRequestIDKey struct{}
|
|
|
|
// ctxExtraFieldsKey is the context key for arbitrary logging fields.
|
|
type ctxExtraFieldsKey struct{}
|
|
|
|
// WithRequestID adds a request correlation ID to the context.
|
|
func WithRequestID(ctx context.Context, id string) context.Context {
|
|
return context.WithValue(ctx, ctxRequestIDKey{}, id)
|
|
}
|
|
|
|
// GetRequestID retrieves the correlation ID from the context.
|
|
// Returns "" if not present or if ctx is nil.
|
|
func GetRequestID(ctx context.Context) string {
|
|
if ctx == nil {
|
|
return ""
|
|
}
|
|
id, _ := ctx.Value(ctxRequestIDKey{}).(string)
|
|
return id
|
|
}
|
|
|
|
// WithField adds a single key-value pair to the context for logging.
|
|
func WithField(ctx context.Context, key string, value any) context.Context {
|
|
return WithFields(ctx, map[string]any{key: value})
|
|
}
|
|
|
|
// WithFields adds multiple key-value pairs to the context for logging.
|
|
// Merges with any existing fields — does not overwrite the whole map.
|
|
func WithFields(ctx context.Context, fields map[string]any) context.Context {
|
|
existing, _ := ctx.Value(ctxExtraFieldsKey{}).(map[string]any)
|
|
newMap := make(map[string]any, len(existing)+len(fields))
|
|
for k, v := range existing {
|
|
newMap[k] = v
|
|
}
|
|
for k, v := range fields {
|
|
newMap[k] = v
|
|
}
|
|
return context.WithValue(ctx, ctxExtraFieldsKey{}, newMap)
|
|
}
|