Request Logging: Tracing Production Requests with Correlation IDs
Why correlation IDs and structured logs are essential for request logging. A practical look at Fastify, Express, pino, and AsyncLocalStorage for production debugging.
In distributed systems where a single request passes through multiple services, debugging production failures becomes nearly impossible without a shared identifier linking related log lines together. This piece explains how structured logging—where every log line is a JSON object with fixed fields—combined with a correlation id like requestId or traceId makes logs actually queryable instead of relying on blind grepping. In the Node.js ecosystem, Fastify ships with pino built in and auto-generates req.id, while Express requires wiring this manually via the pino-http middleware.
The third and most commonly broken piece is propagating that id across async work and downstream HTTP calls. Node's built-in AsyncLocalStorage carries context without threading parameters manually, while W3C Trace Context (the traceparent header) and OpenTelemetry standardize this across services automatically. The content walks through three concrete production failure modes with code: missing correlation ids that make cross-service tracing impossible, unstructured plain-text logs that can't be queried by log aggregators, and logging raw request bodies/headers that leaks secrets like Authorization headers, cookies, or passwords. For the last issue, pino's redact option is shown masking sensitive fields at the logger level rather than relying on call-site discipline.
The practical takeaway for engineers: correlation ids paired with structured logging are what let a team answer 'which service failed and why' in minutes during an incident, instead of spending hours manually correlating scattered log entries.