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/
1.8 KiB
ADR-001: log/slog as the Logging Backend
Status: Accepted Date: 2026-03-18
Context
Structured logging is a cross-cutting concern required by nearly every module in
the ecosystem. External logging libraries (zerolog, zap, logrus) add transitive
dependencies, pin dependency versions, and require every module that accepts a
logger to either import the concrete library or define an adapter interface. Go 1.21
shipped log/slog as a stdlib structured logging API, providing JSON and text
handlers, level filtering, and attribute chaining with no external dependencies.
Decision
logz wraps log/slog exclusively. The concrete type slogLogger holds a
*slog.Logger. New(opts Options) Logger constructs either a JSON handler
(slog.NewJSONHandler) or a text handler (slog.NewTextHandler) backed by
os.Stdout, controlled by Options.JSON.
Options exposes:
Level slog.Level— minimum log level (zero value =slog.LevelInfo).JSON bool— JSON vs text output.StaticArgs []any— key-value pairs attached to every record viaslog.Logger.With.
The slog dependency is internal to the logz package. Consumers depend only on
the logz.Logger interface and are not required to import log/slog at all.
Consequences
- Zero external dependencies:
logzstays at Tier 1 (stdlib only). - The
slogstructured attribute system (slog.String,slog.Int, etc.) is available internally but is not exposed through theLoggerinterface — callers pass plainkey, valuepairs, whichsloghandles via itsanyvariadic. - Output always goes to
os.Stdout. Log routing (to files, remote sinks) is the responsibility of the process supervisor or log collector, not this package. - If a future Go version modifies the
slogAPI, onlylogzneeds to be updated — all consumers remain unaffected.