Structured Logging: The One Observability Investment That Paid Off
1 min read
The before
2025/12/20 14:23:01 ERROR: failed to fetch user
What user? What request? What service? grep through 10,000 lines to find out.
The after
{"level":"error","time":"2025-12-20T14:23:01Z","trace_id":"abc123",
"service":"user-svc","user_id":"usr_abc123","error":"connection refused",
"duration_ms":2503}
The implementation
// slog structured logging (Go 1.21+)
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
// Attach request-scoped fields
r = r.WithContext(slog.NewContext(r.Context(),
logger.With("trace_id", traceID, "request_id", reqID)))
What changed
- Debugging: 15 minutes → 2 minutes
- On-call fatigue: down significantly
- We could finally build dashboards from log fields
What I learned
Structured logging is the highest ROI observability investment you can make. It doesn’t require a vendor, a platform, or a dedicated team — just a JSON encoder and a convention. Everything else (metrics, tracing) builds on top of it.