Propagate a Correlation ID Across Services
Generate a request identifier at the edge, forward it in headers and logs, and return it to the client so a single trace can be reconstructed later.
Task
Carry one correlation id through synchronous calls, queue messages, and logs for cross-service tracing.
When to use this
An error is reported by a user, but no service can join its logs to the upstream request that triggered the failing call.
Prerequisites
- An edge layer that can set response headers
- A structured logging library
Steps
1. Accept an incoming correlation header or generate a new one when absent.
curl -H "x-correlation-id: abc123" https://api.example.com/itemsExpected output: Every request has a non-empty correlation id at the edge.
2. Attach the id to a per-request logging context and echo it in the response header.
Expected output: Log lines for one request share the same id.
3. Forward the id on outbound HTTP calls and as a message attribute for async work.
Expected output: Downstream services inherit the same id.
4. Bound the id length and reject client-supplied ids that are too long.
Expected output: Ids stay bounded and spoof-resistant.
Troubleshooting
Problem
Id missing in async handlers
Solution
Rebuild the log context inside the consumer from the message attribute; do not rely on thread-local state.
Problem
Id collisions
Solution
Use a cryptographically random UUID rather than timestamps plus counters.
Limitations
- A correlation id identifies a request, not a user, so it must not be treated as an auth token.
- Sampling may drop some correlated logs if the pipeline does not keep whole traces.
Context
Carry one correlation id through synchronous calls, queue messages, and logs for cross-service tracing. An error is reported by a user, but no service can join its logs to the upstream request that triggered the failing call.
Steps
- Accept an incoming correlation header or generate a new one when absent. Expected: Every request has a non-empty correlation id at the edge.
- Attach the id to a per-request logging context and echo it in the response header. Expected: Log lines for one request share the same id.
- Forward the id on outbound HTTP calls and as a message attribute for async work. Expected: Downstream services inherit the same id.
- Bound the id length and reject client-supplied ids that are too long. Expected: Ids stay bounded and spoof-resistant.
const cid = req.headers['x-correlation-id'] ?? crypto.randomUUID();
logger.child({ correlationId: cid }).info('request.start');
res.setHeader('x-correlation-id', cid);
Verify
- Every request has a non-empty correlation id at the edge.
- Log lines for one request share the same id.
- Downstream services inherit the same id.
- Ids stay bounded and spoof-resistant.
Limits
- A correlation id identifies a request, not a user, so it must not be treated as an auth token.
- Sampling may drop some correlated logs if the pipeline does not keep whole traces.