Files
httpmw/docs/adr/ADR-001-logz-context-import-exception.md
Rene Nochebuena ad2a9e465e feat(httpmw): initial stable release v0.9.0
Standalone net/http middleware for panic recovery, CORS, request ID injection, and request logging.

What's included:
- Recover(): panic -> 500, captures debug.Stack, no logger required
- CORS(origins): OPTIONS 204 preflight, origin allowlist, package-wide method/header constants
- RequestID(generator): injects ID via logz.WithRequestID, sets X-Request-ID response header
- RequestLogger(logger): logs method/path/status/latency/request_id; Error for 5xx, Info otherwise
- Logger interface: Info, Error, With — duck-typed; satisfied by logz.Logger
- StatusRecorder: exported http.ResponseWriter wrapper that captures written status code
- Direct logz import for context helpers (documented exception to ADR-001)

Tested-via: todo-api POC integration
Reviewed-against: docs/adr/
2026-03-19 00:03:24 +00:00

2.5 KiB

ADR-001: Direct logz Import for Context Helpers (Exception to Global ADR-001)

Status: Accepted Date: 2026-03-18

Context

Global ADR-001 states that modules receiving a logger from the application layer should duck-type the logger injection point with a private interface (e.g. type Logger interface { Info(...); Error(...) }), so the application is not forced to import logz just to satisfy a concrete type.

httpmw follows this rule for its Logger interface in logger.go. Any struct with the right method set satisfies it.

However, httpmw also calls logz.WithRequestID (in requestid.go) and logz.GetRequestID (in logger.go) to store and read the request ID from context. These functions use an unexported context key type defined inside logz:

// inside logz — not exported
type contextKey string
const requestIDKey contextKey = "request_id"

A context value stored with that key can only be read back with the same key. If httpmw tried to replicate the storage using its own unexported key, the values set by logz.WithRequestID would be invisible to logz.GetRequestID, and vice-versa — breaking the downstream logz.Logger.WithContext integration.

Decision

httpmw imports logz directly for context helper access (logz.WithRequestID, logz.GetRequestID). This is an explicit, documented exception to the duck-typed Logger rule in global ADR-001.

The Logger injection point remains duck-typed (httpmw.Logger interface in logger.go). The exception applies only to the two context functions, not to the logger parameter of RequestLogger.

Consequences

  • httpmw has a direct module dependency on logz in its go.mod. Upgrading logz is a breaking change for httpmw if the context helper signatures change.
  • logz.WithRequestID and logz.GetRequestID form a shared context contract between httpmw (which stores the ID) and logz.Logger (which reads it when enriching log records). Both sides of the contract must be the same package.
  • Applications that use httpmw without logz as their logger will still get request IDs injected into the context correctly — RequestID middleware works independently. The ID simply won't be picked up automatically by a non-logz logger.
  • This exception must not be used as a precedent for importing logz elsewhere without justification. The rule remains: duck-type logger injection; import logz only when the unexported context key contract requires it.