Distributed tracing: one trace id follows a request through ten services, with a span tree waterfall below
One id to JOIN · a span tree to ORDER · one sampling decision at the edge
Incident Response & Operations

The One Field That Turns Ten Log Files Into One Story

Correlation IDs, spans & the traceparent header — the mechanism that makes a cross-service failure reconstructable

Core Concept

Inside one process, the language hands you the causal chain for free: you read a stack trace. Cross one network boundary and that chain is gone — service B has no idea which of A’s requests caused the call it is serving. Ten services produce ten log files with no join key between them. The fix is not better logging; it is one identifier, generated at the edge, propagated through every hop in a request header, and stamped on every log line. One grep — the whole request.

Key Components

The join keyCorrelation ID (trace-id) One identifier per request, minted at the edge. Every service logs it on every line, so the ten log files gain the one column they were missing.
The standardtraceparent header W3C Trace Context (a Recommendation, not a draft). Carries trace-id plus parent-id — exactly the id-to-join plus tree-to-order pair.
The treeSpan ID + parent span ID A span per operation. The parent link turns “all ten services logged something” into “B waited 900 ms on C” — who called whom, with durations.
The agreementEdge sampling One decision at the edge: is this request being recorded? Every hop must agree. If they don’t, the waterfall is silently incomplete.

How It Works

  1. Mint one trace id at the edge (gateway) for every incoming request.
  2. Propagate it in the request headers through every hop — the W3C traceparent format carries trace-id (join key) and parent-id (which span called it).
  3. Every service logs the id on every line. Now one grep returns the whole request across all ten services.
  4. Each service also records a span per operation, linked by parent id — the flat log list becomes a span tree you can read as a waterfall.
  5. Make the sampling decision once, at the edge, so every service agrees whether this request is being recorded.

What Breaks It

1 · The hop that drops the header Any hop that drops traceparent silently severs the trace — no error, just a missing segment. Usual culprits: a thread pool, a queue, or a retry that starts a fresh request. The gap is the defect.
2 · Clock skew & sampling Spans are stamped by different machines, so a child can appear to start before its parent — trust the parent/child structure, not wall-clock order. And most systems sample a small fraction, so the failure that paged you is often the one that wasn’t sampled (hence tail-based sampling: decide after you see the error).
3 · Unstructured lines & the bill An id on every line proves nothing if the lines are prose you can’t query. And propagation touches every service — log volume plus retention is the cost people underestimate.

Real-World Applications

Checkpoint — answer before you move on

  1. A request crosses five network boundaries and fails inside the fifth service. Why is a stack trace no longer enough, and which single identifier lets you reconstruct the whole request from ten log files? One process = free causal chain; one boundary = no join key.
  2. What does the W3C traceparent header carry, and why is “who participated” not the same as “who called whom”? trace-id joins; span id + parent span id orders.
  3. Name two ways a trace gets silently severed mid-request, and explain why the request that paged you is often missing from the traces. Thread pools, queues, retries … and head-based sampling.
Test it: pick a real production request id and reconstruct its full path end to end. If any hop is missing, that hop is where propagation broke — you have found a defect, not confirmed a design.