Stackbook Logo
observabilityestablished · low operational burden

Correlation IDs

Also known as: request-ids, trace-context-propagation

Intent

Attach a unique identifier to each request and propagate it through every hop so related logs and events can be joined.

Problem

One user action fans out across many services, queues, and databases, each writing separate logs. Without a shared key, reconstructing what happened to a single request means guessing from timestamps.

Forces

  • •Requests cross process and protocol boundaries
  • •Asynchronous hops lose natural context
  • •Identifiers must be cheap to generate and carry
  • •Untrusted clients may supply bad values

Solution

Overview

A correlation ID is a unique token that follows a request across services. Every log line, span, and event produced for that request carries the same value, so a single search reconstructs the story. It is the cheapest form of distributed observability.

How it works

At the edge, middleware reads the header or creates a new identifier, then puts it in context. Outgoing clients read it from context and send it onward.

client -> gateway: (none)
gateway: id = new_id()            # 7f3a9c...
gateway -> orders:   X-Request-ID: 7f3a9c
orders  -> payments: X-Request-ID: 7f3a9c
orders  -> queue:    message header request_id=7f3a9c
every log: {"request_id":"7f3a9c","svc":"orders","msg":"..."}

For asynchronous work, put the ID in message headers and restore it in the consumer's context. If one message triggers many, keep the original as a parent or causation ID and mint a new ID per child so that the relationship is still traceable. Return the ID in error responses and show it in the UI error page, so support can search logs immediately.

When tracing is deployed, use the W3C traceparent header; the trace ID then doubles as the correlation ID and links logs to spans.

Pitfalls

  • Trusting any inbound value. Validate length and character set to prevent log injection.
  • Losing context in thread pools, async callbacks, or goroutines. Pass context explicitly or use a context-propagating library.
  • Using the ID as a metric label. Its cardinality is unbounded.
  • Inconsistent header names across teams, which break the chain.

Key takeaways

  • Create at the edge, propagate everywhere, log always.
  • Carry it through queues, not just HTTP.
  • Return it to users for support.
  • Align with trace context to unify logs and traces.

✓ When to Use

  • •Microservice or event-driven systems
  • •Support workflows where users report an error
  • •Joining logs across services during incidents

✗ When Not to Use

  • •Single process applications with a request-scoped logger
  • •Storing it as a replacement for authenticated user identity
  • •Using it as a metric label

Pros

  • +Joins logs and events for a single request
  • +Cheap to implement
  • +Gives support a handle to quote

Cons

  • −Needs discipline at every hop
  • −Does not capture timing or hierarchy by itself
  • −Async fan-out can blur the notion of one request

Cost Profile

Infrastructure

Low - header propagation

Operational

Low - library or middleware

Cognitive

Low - simple concept

Failure Modes

  • Identifier dropped at an async boundary, breaking the chain

  • Client-supplied value injected into logs, enabling log forging

  • IDs reused across retries so distinct attempts cannot be told apart

Real-World Examples

Alternatives

    Related Patterns

    • distributed-tracing
    • structured-logging
    • observability

    Competency Domains

    reliability opsdistribution communication