Skip to content

feat(observability): structured logging foundation and error taxonomy - #30

Open
kiro-agent[bot] wants to merge 1 commit into
mainfrom
feat/observability-logging-foundation
Open

feat(observability): structured logging foundation and error taxonomy#30
kiro-agent[bot] wants to merge 1 commit into
mainfrom
feat/observability-logging-foundation

Conversation

@kiro-agent

@kiro-agent kiro-agent Bot commented Jul 28, 2026

Copy link
Copy Markdown

This pull request was created by @kiro-agent on behalf of @ccrsxx 👻

Comment with /kiro fix to address specific feedback or /kiro all to address everything.
Learn about Kiro Web


Summary

First PR in the observability roadmap (Phase 0/1 prerequisite). Makes the API's logs immediately useful in Loki/Grafana and lays the correlation groundwork for tracing. Additive, no new dependencies, no behavior change for clients.

Logging

  • Drop the GCP Cloud Logging severity/message renaming (leftover from Cloud Run). Keeps slog defaults (level/msg/time) so Grafana level detection and Logs Drilldown work.
  • Decouple format from env via LOG_FORMAT (json|text) so the JSON pipeline can be tested locally instead of only in prod.
  • ContextHandler slog middleware enriches every log line with trace_id/span_id/request_id/route pulled from the request context — existing slog.*Context calls across all feature packages become correlated for free.
  • Base attributes service_name + deployment_environment on every line (label convention).

Request correlation

  • RequestID middleware (registered outermost): reads a W3C traceparent, an inbound X-Request-Id, or generates a UUID; echoes X-Request-Id back so users can quote it when reporting issues.

Request logging middleware

  • duration_ms is now a number (was the string "1.5ms", unqueryable).
  • Logs the low-cardinality matched route (r.Pattern) alongside the raw path, plus response bytes.
  • Skips /sse so long-lived streams don't corrupt latency aggregations.

Error taxonomy (Loki-based error reporting foundation)

  • Severity by status class: 5xx → error, 429 → info (expected/high-volume), other 4xx → warn. So level="error" finally means "my fault".
  • Stable error_kind fingerprint for grouping: HTTPError.Codehttp_<status> → Go type name for unhandled → first application stack frame for panics.
  • Optional Code field added to HTTPError (backwards compatible; all call sites use named literals).

Testing

  • go build ./...
  • go test ./... ✅ all packages pass
  • go vet ✅ / gofmt ✅ clean
  • Note: golangci-lint could not run in the sandbox (its binary was built with Go 1.24, below this repo's 1.26 target). Please confirm CI lint locally.
  • No new tests added (per request); existing tests unchanged and green.

Follow-ups (separate PRs)

  • OpenTelemetry tracing + otelpgx + client transports (adds deps)
  • /metrics + /healthz on an internal-only port, Go runtime + custom SSE/cache/upstream metrics
  • homelab repo: LGTM stack (docker/observability + docker/observability-agent)

…nomy

- drop GCP Cloud Logging severity/message renaming; keep slog defaults
  (level/msg/time) so Grafana/Loki level detection works
- decouple log format (LOG_FORMAT) from APP_ENV so the JSON pipeline can
  be exercised locally
- add ContextHandler that enriches every log line with trace_id/request_id
  from the request context
- add RequestID middleware (outermost) that reads W3C traceparent or
  generates a request id and echoes X-Request-Id
- logging middleware: numeric duration_ms, low-cardinality route vs raw
  path, response bytes, skip SSE, context-aware
- error taxonomy: severity by status class (5xx error / 429 info / 4xx
  warn) and a stable error_kind fingerprint for log-based error grouping
- add optional Code field to HTTPError for machine-readable error kinds
- config: add observability env vars with safe defaults (no new required)

Co-authored-by: Ami <github@ccrsxx.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Ready

Development

Successfully merging this pull request may close these issues.

2 participants