• v0.9.0 3667b92fab

    Rene Nochebuena released this 2026-03-18 13:33:16 -06:00 | 0 commits to main since this release

    v0.9.0

    code.nochebuena.dev/go/logz

    Overview

    logz provides a stable Logger interface backed by the stdlib log/slog package, with no external logging library dependencies. It owns the request correlation ID context key and an extra-fields context key, supplying helpers to attach and retrieve them so any downstream handler can enrich log records from the context without manual field threading. When an error passed to Logger.Error implements the ErrorCode() or ErrorContext() duck-type interfaces, the structured fields are automatically appended to the log record — without logz importing xerrors.

    What's Included

    • Logger interface — Debug, Info, Warn, Error(msg, err, args...), With, WithContext
    • Options struct — Level slog.Level, JSON bool, StaticArgs []any
    • New(opts Options) Logger — constructs a Logger writing to os.Stdout; zero value of Options is valid (INFO level, text output)
    • WithRequestID(ctx, id string) context.Context — stores a correlation ID in the context; owned by this package
    • GetRequestID(ctx context.Context) string — retrieves the correlation ID; returns "" if absent or ctx is nil
    • WithField(ctx, key string, value any) context.Context — adds a single key-value logging field to the context
    • WithFields(ctx context.Context, fields map[string]any) context.Context — merges multiple key-value fields into the context; does not overwrite existing fields
    • Automatic error_code and context-field enrichment in Logger.Error via duck-typed errorWithCode / errorWithContext private interfaces

    Installation

    require code.nochebuena.dev/go/logz v0.9.0
    

    Design Highlights

    • Wraps log/slog exclusively — no external logging library is required or vendored (see docs/adr/ADR-001-slog-stdlib-backend.md).
    • logz owns the context keys ctxRequestIDKey{} and ctxExtraFieldsKey{} as unexported struct types, preventing collisions with any other package's context keys (see docs/adr/ADR-002-requestid-context-ownership.md).
    • New returns the Logger interface, not the concrete *slogLogger, and With also returns Logger — the concrete type is never exported, making the interface the only public surface and allowing safe local re-declaration in libraries (see docs/adr/ADR-003-exported-logger-interface.md).
    • Error treats err as a first-class parameter (not a variadic arg), enabling the automatic duck-typed enrichment path; xerrors.Err satisfies this without any import between the two packages.

    Known Limitations & Edge Cases

    • All log output is always written to os.Stdout. There is no configurable io.Writer — routing to files, syslog, or other sinks must be handled by the process supervisor or a shell redirect.
    • There is no log sampling or rate limiting. A high-frequency error loop will produce one log record per call with no throttling.
    • WithField and WithFields create a new map and a new context value on every call. In hot paths (e.g. per-request middleware), repeated calls accumulate allocations; batch them with a single WithFields call where possible.
    • WithContext(nil) is handled safely (returns the same logger), but WithRequestID and WithField do not accept nil contexts and will panic.
    • StaticArgs in Options are attached at logger construction time only; there is no way to inject static args into an existing logger without creating a new one via With.
    • Logger.With returns a Logger interface — if the caller needs WithContext after With, that is supported, but the full method set of slogLogger is not accessible after With.

    v0.9.0 → v1.0.0 Roadmap

    • Evaluate whether a configurable io.Writer (passed via Options) is required for production use cases such as test log capture or file-based audit trails.
    • Decide whether log sampling belongs in this package or should be left to slog.Handler wrappers, and document the recommended pattern.
    • Validate the duck-typed xerrors enrichment path under concurrent production load to confirm no race conditions on the ErrorContext() map read path.
    • Confirm that the Logger interface method set is final before 1.0; any addition is a breaking change for all callers that re-declare the interface locally.
    • Achieve production validation of the WithFields merge semantics under concurrent middleware chains.

    v0.9.0 rationale: The API is stable and intentional — designed through multiple architecture reviews and tested end-to-end via the todo-api POC (SQLite, RBAC, middleware stack, HTTP handlers). The module is not yet battle-tested in production for all edge cases, and the pre-1.0 designation preserves the option for minor API refinements based on real-world use.

    Downloads