Logging & Structured Logs
Write the truth as structured events
Logs are the event record; structured logs (JSON) are searchable, alertable, and correlate with traces. The craft: request/context details, small logging budgets, sampling, and architecture from app → collector → store → SIEM.
What you will learn
- Emit structured JSON logs with correlation ids
- Manage verbosity, sampling and retention
- Pipeline logs agent → store → alert/SIEM
Structured logs = one JSON object per event with level, ts, service, trace-id, payload. Each line is auto-searchable/alertable. Design: never log secrets; log 'what happened around the request', add context; balance verbosity vs cost with sampling.
What is this?
Libraries: pino, logrus, winston with pino, serilog; OpenTelemetry + logger bridge. Fields: ts, level, msg, service, trace_id, span_id, duration_ms, status, error/warn payload. Levels: debug/info/warn/error/fatal with env-variable gating. Multi-line export, log shaping middleware (request-id propagation). Pipelines: app → stdout → collector (filebeat/fluent-bit/OTel) → store (CloudWatch, Loki, Azure Log Analytics, GCP Logging) → queries/alerts/SIEM. Retention/tiering: hot (7-30d) / cold (30-90d) / archival (S3). Sampling: head (record % across the fleet) + tail (by result/error) to keep bill sane. Correlate with traces via trace-id.
Why do you need it?
- Wrecked debugging is 90% solved by 'which trace-id and what did the log say'.
- Structured logs turn 'grep the string' into SQL-like queries and alerts.
- Logs bridge the gap: metrics say latency; logs say which code path + why.
- Good log hygiene is the cheapest forensic evidence you have.
Mental model
A pilot's flight log: every entry time-stamped, machine-readable, tied to the flight number
Structured logs
Pen-and-paper journals are searchable only by eye; structured flight logs link directly to lineage (trace-id) and can be queried on 'any hard landing after 3pm' — instantly.
How it works
- Middleware injects a trace-id + request context into each log call.
- Logger writes a single JSON line to stdout with that context.
- Agent/collector ships to the store; queries and alert rules index it.
- Sampling keeps volume bounded while preserving error/incident lines.
- Retention per tier; SIEM cross-references adjacent sources.
Log pipeline.
Examples
Structured log shape (pino-ish)
{"level":30,"time":1730000000000,"msg":"order failed", "service":"checkout","trace_id":"4f3a11...","duration_ms":84, "order_id":"o-123","error":{"code":"INV","detail":"out of stock"}}Every field is queryable; the trace-id lets you pull the full request journey from the trace pillar.
In production
- Request-scoped logging middleware everywhere (one standard context).
- Alerting on structured log criteria (e.g., specific error code with rate).
- Sampling: keep 100% of 5xx/errors; head-sample debug back to ~10%.
Best practices
- Log facts, not feelings; include operation parameters where useful (never secrets/raw tokens).
- Level-gate by environment; prod defaults to info-with-error-fill.
- One JSON per line; avoid heredoc/multiline strings breaking parsers.
- Set retention + tier by data class (audit/prod-events longest).
- Correlate with traces: same trace-id in both pillars always.
Common mistakes
- Logging secrets/whole payloads (the incident-record you'd rather forget).
- Textual ratt-tattle: too many log lines with zero structure or use.
- Log aggregation with no indexing/retention tuning → big bill for noise.
- Sampling 5xx related info out (you just thinned the forensic blood).
Debugging tips
- Missing logs for a request → collector buffer or sampling; check trace-id-to-log join.
- JSON parse errors in the sender → the store's parser saw multi-line/passthrough; fix emitter.
- Too slow to query → add partition by service/time; avoid scans over the whole store.
- Gaps in timeline correlate with collector downtime vs real silence — check the collector's state.
Interview checkpoint
Hands-on lab
- Emit JSON logs from a tiny service
- Ship to a local store (Loki/Docker) or cloud
- Run a correlation query with trace-id
Emit, Collect, and Query Structured Logs
30 min · intermediate
Quick quiz
The killer feature of structured logs is:
When logging a failing request you should always include: