A trace ID is a globally unique identifier that every span of one distributed trace shares. In W3C Trace Context and OpenTelemetry it is 16 bytes, written as 32 lowercase hex characters, and travels between services in the traceparent header, so that backends can join spans, logs and metrics exemplars from the same request.
What is a trace ID?
A trace ID is the identifier that ties together all work done for one request as it moves through a distributed system. The first service that receives a request without incoming context generates a new trace ID; every downstream span, whether in an API gateway, a payment service or a database client, records the same trace ID plus its own span ID. A tracing backend can then assemble all spans with that trace ID into a single trace tree.
Each span additionally stores its parent span ID, which defines the tree structure. The trace ID answers "which request does this belong to?", the span ID "which operation is this?".
The W3C trace ID format
The W3C Trace Context recommendation, which OpenTelemetry uses by default, specifies the format precisely:
| Field | Size | Encoding |
|---|---|---|
| trace-id | 16 bytes (128 bit) | 32 lowercase hex characters |
| parent-id (span ID) | 8 bytes (64 bit) | 16 lowercase hex characters |
| trace-flags | 1 byte | 2 hex characters, bit 0 = sampled |
An ID consisting only of zeros is invalid for both trace ID and span ID. The IDs are carried in the traceparent HTTP header, which has the form version-traceid-parentid-flags:
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
Here 00 is the version, 4bf9…4736 the trace ID, 00f0…02b7 the span ID of the calling span and 01 means the trace was sampled. Vendor-specific data goes into the separate tracestate header. The specification recommends generating trace IDs randomly so they are unique without coordination; the Level 2 draft of Trace Context adds a "random" flag that signals this property to downstream samplers.
How trace IDs propagate
Trace IDs travel through context propagation. Instrumentation reads traceparent from incoming requests, stores it in the in-process context and injects it into outgoing HTTP calls, gRPC metadata or message headers on Kafka or other queues. If any hop drops the header, for example a proxy that strips unknown headers or a custom HTTP client without instrumentation, the downstream service starts a new trace ID and the trace breaks into two disconnected pieces. Older formats such as Zipkin's B3 headers, which also allow 64-bit trace IDs, or AWS X-Ray's own header can be bridged with OpenTelemetry propagators.
Putting the trace ID into logs
The trace ID is the simplest way to correlate signals. When every log line written inside a span includes trace_id and span_id, you can jump from a slow trace to exactly the logs it produced, and from an error log to the full request path. With structured logging this is a single field:
{"level":"error","msg":"card declined","service":"payments",
"trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","span_id":"00f067aa0ba902b7"}
OpenTelemetry log bridges for Java, Python, Go and other languages add these fields automatically. Metrics can link to traces as well: an exemplar attaches a sample trace ID to a histogram bucket, so a latency spike on a chart leads straight to a representative trace.
Best practices
- Use W3C Trace Context end to end and configure every proxy, gateway and load balancer to forward
traceparentandtracestate. - Return the trace ID in error responses or a response header, so support teams can look up a user's failing request.
- Never derive trace IDs from user data or sequential counters; use the SDK's random generator.
- Do not use trace IDs as metric labels; every value is unique, which causes a cardinality explosion.
- Remember that sampling may drop a trace even though its ID appears in logs.
Common mistakes
- Uppercase hex or IDs of the wrong length, which compliant parsers reject.
- Starting a new trace in every service because a message queue consumer ignores the propagated context.
- Logging a trace ID that comes from a different context than the active span, for example in asynchronous callbacks.
Trace IDs across asynchronous work
Not every relationship fits a single trace. A batch job that processes 500 queued messages, each produced by a different request, cannot have one parent. OpenTelemetry handles this with span links: the consumer span starts its own trace, or continues one, and links to the span contexts of the producers, each identified by its trace ID and span ID. Long-running workflows that span hours or days are often split into several traces on purpose and connected with links or a shared business identifier, such as an order ID recorded as a span attribute. This keeps individual traces a manageable size while still letting you navigate from one stage to the next.
How Obseria helps
Obseria ingests OpenTelemetry traces natively via OTLP and stores spans, logs and metrics in Obseria Lake, so a trace ID query returns the full trace together with its logs. In distributed tracing views you can pivot from any span to correlated logs and errors. With existing OpenTelemetry SDKs, the first trace typically arrives within 15 minutes.