What goes in a log line and how to make sure the right fields are there without having to remember.
Structured logging
- Consistent shape, typically key-value pairs
- Doesn't have to be JSON, but JSON is the standard (ELK, Loki, CloudWatch parse it natively)
- In Go,
slogis structured by default. Every attribute (slog.String,slog.Int,slog.Duration) becomes a typed key
Slog groups
Group related attributes under one key. Produces nested JSON or dotted text keys, both queryable.
logger.Info("user logged in",
slog.Group("artist",
slog.String("name", "Thebe Kgositsile"),
slog.String("stage_name", "Earl Sweatshirt"),
slog.String("group", "Odd Future"),
),
)
Use it for errors. Group message, type, and stack trace under one error key. In linko: internal/logging/logger.go:89-102, every error log is grouped into {message, stack_trace, ...extra_attrs}.
What context every log needs
- Machine, host, container, pod
- Request, request ID
- User, when authenticated
- Service, which service in the monorepo
Propagating context
- Intra-process,
context.Contextfor request-scoped values, request-scoped*slog.Loggerderived from the root via.With(...). In linko:middleware.go:20andmiddleware.go:30-33define aLogContextstruct that travels on the request context. - Inter-process, propagate a request ID across services via headers. In linko:
middleware.go:51-60, generates or acceptsX-Request-ID, sets it on the response.
See Correlated logging for the full pattern, including Sentry, trace IDs, and frontend/backend join.
Instance context
Bind once at startup, attach to every log: env, hostname, git_sha + build_time (see Foundations for the binding pattern). In linko: main.go:63-68.
For desktop/mobile: also OS, time zone, library versions.
Request logging middleware
The cleanest way to log requests in an HTTP service. One middleware that:
- Records start time
- Wraps the response writer to capture status + bytes
- Calls
next.ServeHTTP - Emits one structured
Served requestlog with method, path, status, duration, request_id, user (if any), error (if any)
In linko: middleware.go:118-150. IP redaction (middleware.go:160-171) means /24 only, no full client IP.