Problem
Logging is fragmented across 3 libraries with no standard:
| Logger |
Services |
log.Printf() (unstructured) |
identity, keys, memory, meter, audit, registry |
log/slog (structured) |
llm-gateway, prompts, parker, service-runtime |
zerolog (structured) |
gate |
Six services use the standard library's log package — unstructured text to stderr. In production this means:
- Can't filter by log level
- Can't search by structured field (org ID, request ID, user ID)
- Can't correlate logs across requests
- Log aggregation tools (Loki, CloudWatch, etc.) can't parse them
Proposal
- Standardize on
log/slog (stdlib, no extra dependency, already used by 4 services + service-runtime)
- Provide a
NewLogger() factory in service-runtime that configures:
- JSON handler in production (
ENVIRONMENT=production)
- Text handler in development
- Default attributes: service name, version, environment
- Require request-scoped logging via middleware that injects request ID, org ID, and actor into the slog context
- Migrate gate from zerolog to slog (or accept the divergence — gate is already structured)
Migration path
- service-runtime already has slog primitives in
observability/logging.go
- Replace
log.Printf() → slog.Info() / slog.Error() with structured fields
- Each service migration is a small PR (mostly find-and-replace)
Services to migrate
Problem
Logging is fragmented across 3 libraries with no standard:
log.Printf()(unstructured)log/slog(structured)zerolog(structured)Six services use the standard library's
logpackage — unstructured text to stderr. In production this means:Proposal
log/slog(stdlib, no extra dependency, already used by 4 services + service-runtime)NewLogger()factory in service-runtime that configures:ENVIRONMENT=production)Migration path
observability/logging.golog.Printf()→slog.Info()/slog.Error()with structured fieldsServices to migrate