patternjavascriptModerate
Correlation IDs: tracing requests across services and logs
Viewed 0 times
correlation IDrequest IDdistributed tracingAsyncLocalStoragex-correlation-idlog context
Problem
A request touches 5 services and fails. Finding all related log entries across services requires searching by timestamp range and guessing which logs belong to the same request.
Solution
Propagate a correlation ID through all service calls and log it with every entry:
import { AsyncLocalStorage } from 'node:async_hooks';
import { v4 as uuidv4 } from 'uuid';
// Store correlation ID in async context (no thread-local in Node.js)
const correlationStorage = new AsyncLocalStorage();
// Middleware: create or propagate correlation ID
app.use((req, res, next) => {
const correlationId = req.headers['x-correlation-id'] || uuidv4();
res.setHeader('x-correlation-id', correlationId);
correlationStorage.run({ correlationId }, next);
});
// Helper to get current correlation ID
const getCorrelationId = () => correlationStorage.getStore()?.correlationId;
// Logger automatically includes it
const logger = pino({
mixin: () => ({ correlationId: getCorrelationId() }),
});
// Forward to downstream services
async function callUserService(userId) {
return fetch(`http://user-service/users/${userId}`, {
headers: {
'x-correlation-id': getCorrelationId(),
},
});
}Why
AsyncLocalStorage provides Node.js equivalent of thread-local storage, propagating context through async call chains without passing it as a parameter to every function.
Gotchas
- AsyncLocalStorage context is not automatically propagated across message queue consumers — re-extract the ID from the message payload
- Use a standard header name: x-correlation-id or x-request-id (pick one and use it everywhere)
- Correlation IDs should be set by the entry point (API gateway or first service) — not generated by each service
- When calling external services you don't control, still log the correlation ID on your side
Revisions (0)
No revisions yet.