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/
3.9 KiB
logz
Structured logging backed by
log/slogwith automatic error enrichment.
Module: code.nochebuena.dev/go/logz
Tier: 1 — stdlib only (log/slog, context, errors, os)
Go: 1.25+
Dependencies: none
Overview
logz wraps log/slog behind a simple Logger interface. It adds two things on top of plain slog:
- Automatic error enrichment —
Errorinspects the error forErrorCode()andErrorContext()methods and appends the code and context fields to the log record automatically. This pairs withxerrors.Errwithout importingxerrors. - Context propagation helpers —
WithRequestID,WithField,WithFieldsstore values incontext.Context;WithContextcreates a child logger pre-loaded with those values.
Installation
go get code.nochebuena.dev/go/logz
Quick start
import (
"log/slog"
"code.nochebuena.dev/go/logz"
)
logger := logz.New(logz.Options{
Level: slog.LevelInfo,
JSON: true,
StaticArgs: []any{"service", "api"},
})
logger.Info("server started", "port", 8080)
logger.Error("request failed", err)
Usage
Creating a logger
// Zero value: INFO level, text output, no static args.
logger := logz.New(logz.Options{})
// Production: JSON, custom level, static service tag.
logger := logz.New(logz.Options{
Level: slog.LevelInfo,
JSON: true,
StaticArgs: []any{"service", "payments", "env", "prod"},
})
The library does not read environment variables. Reading LOG_LEVEL or LOG_JSON_OUTPUT is the application's responsibility — pass the parsed values into Options.
Logging
logger.Debug("cache miss", "key", cacheKey)
logger.Info("user created", "user_id", id)
logger.Warn("slow query", "duration_ms", 520)
logger.Error("save failed", err, "table", "orders")
Error automatically enriches the log record when err satisfies the duck-type interfaces:
| Method | What it adds |
|---|---|
ErrorCode() string |
error_code attribute |
ErrorContext() map[string]any |
all key-value pairs in the map |
Child loggers
// Attach fixed attrs to every record from this logger.
reqLogger := logger.With("request_id", id, "user_id", uid)
// Attach attrs stored in context.
reqLogger := logger.WithContext(ctx)
Context helpers
// Store values.
ctx = logz.WithRequestID(ctx, requestID)
ctx = logz.WithField(ctx, "user_id", userID)
ctx = logz.WithFields(ctx, map[string]any{"tenant": "acme", "region": "us-east"})
// Retrieve.
id := logz.GetRequestID(ctx)
// Build a child logger with all context values pre-attached.
reqLogger := logger.WithContext(ctx)
WithFields merges with any existing fields in the context — it does not overwrite them.
Design decisions
No singleton — logz.New(opts) returns a plain value. Each component that needs logging receives a logz.Logger via constructor injection. Tests can create isolated loggers without global state.
Error replaces LogError — enrichment is automatic and zero-overhead when the error is a plain error. Callers need only one method instead of two.
Fatal removed — calling os.Exit(1) inside a library is untestable and bypasses deferred cleanup. Callers log the error then decide how to exit:
logger.Error("fatal startup failure", err)
os.Exit(1)
No env-var reading — libraries should not read environment variables. The application reads LOG_LEVEL/LOG_JSON_OUTPUT and passes parsed values into Options.
Duck-typing bridge — logz defines private errorWithCode and errorWithContext interfaces. xerrors.Err satisfies both structurally — no import of xerrors is needed.
Ecosystem
Tier 0: xerrors
↑ (duck-types — no direct import)
Tier 1: logz ← you are here
↑
Tier 2: httpclient, httputil
↑
Tier 4: httpmw, httpauth, httpserver
License
MIT