Free — no signup required

Context Propagation and Log Correlation

2 min read

The Multi-Service Problem

A single user request in a modern system often touches five or more services. If something goes wrong, you need to find all the log lines — across all those services — that belong to that one request. With unstructured logs, this is nearly impossible. With structured logs and a trace ID, it is a single query.

Trace ID (also called a correlation ID or request ID) is a unique identifier generated at the entry point of a request (usually the API gateway or the first service that receives it) and then passed to every downstream service call. Each service includes this ID in every log line it emits for that request.

Request arrives at API Gateway → generates trace_id: "abc123"
  → Auth Service logs: {"trace_id": "abc123", "event": "token_validated", ...}
  → Payment Service logs: {"trace_id": "abc123", "event": "payment_started", ...}
  → Payment Service logs: {"trace_id": "abc123", "event": "payment_failed", "reason": "timeout"}
  → Notification Service logs: {"trace_id": "abc123", "event": "email_skipped", "reason": "payment_failed"}

Now, when a customer reports that their payment failed, you run one query filtering on trace_id = "abc123", and you get the complete story of that request across every service, in chronological order.

Implementing Context Propagation

In practice, trace IDs are passed via HTTP headers (the X-Request-ID or traceparent header from the W3C Trace Context standard). Each service reads the header on incoming requests and writes it to a thread-local or context variable that the logging library reads automatically.

Python example with FastAPI and structlog: an HTTP middleware reads the trace ID from the incoming request header (or generates a new one if absent), binds it to structlog's context variables so every subsequent log call in that request automatically includes it, forwards the same ID in the outgoing response header so downstream services can correlate, and clears the context once the request completes.

With this middleware in place, every log.info(...) call anywhere in the request handler automatically includes "trace_id": "abc123" — without the developer having to pass it manually.

This is one of 18 chapters

Every chapter is free — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

Sign up free