skip to content

What is trace context propagation, and what does the W3C traceparent header carry across an HTTP call?

level: juniorimportance: must knowfreq 70%

answer

  1. traceparent: version-traceId-parentId-flags
  2. 32-hex trace id, 16-hex span id, sampled = low bit of flags
  3. Micrometer Tracing auto-injects on RestTemplate/WebClient
  4. root service starts a new trace when no header present

basics

~20 s

Context propagation passes the current trace's IDs to the next service so their spans join one trace. With Micrometer Tracing, Spring auto-adds a W3C traceparent header carrying the trace ID, the caller's span ID, and a sampled flag.

solid answer

~40 s

Distributed tracing needs every service to attach its work to the same trace. Trace context propagation is how the caller's identifiers travel to the callee. With Micrometer Tracing (the successor to Spring Cloud Sleuth), Spring Boot auto-instruments RestTemplate/WebClient/RestClient and incoming controllers so the outgoing request carries a W3C traceparent header. Its format is version-traceId-parentId-flags, e.g. 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01: a 32-hex trace ID shared by the whole trace, a 16-hex span ID identifying the caller's span (which becomes the callee's parent), and flags whose low bit is the sampled decision. The receiving service extracts this, creates a child span under the same trace ID, and continues. Without propagation you get many disconnected single-service traces instead of one end-to-end trace.

code

properties · 6 lines
properties
# build.gradle: micrometer-tracing-bridge-otel + opentelemetry exporter
# W3C is the default propagation format in Spring Boot 3
management.tracing.propagation.type=W3C
management.tracing.sampling.probability=1.0
# Default log pattern already includes the correlation IDs:
# %5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]

go deeper

for a junior

Know the term, that Spring adds a traceparent header automatically, and that it carries trace + span IDs plus a sampled flag.

for a middle

Explain the four traceparent fields, that the root service starts the trace, and how IDs reach the log MDC pattern.

for a senior

Discuss auto-instrumentation coverage, context loss across threads/un-instrumented clients, and consistent head sampling via the flag.

for a principal

Reason about standardizing W3C across a fleet, tracestate usage, and boundaries where manual Propagator injection is required.

**The problem.** In a distributed system a single user action fans out across many services. To see it as one timeline you need every service's spans stitched into a single *trace*. A trace is identified by a **trace ID** shared by all its spans; each unit of work is a **span** with its own **span ID** and a pointer to its **parent span ID**. Propagation is the mechanism that carries these identifiers from one process to the next. **Who does it in Spring.** Spring Boot 3 uses **Micrometer Tracing** (which replaced **Spring Cloud Sleuth**) as a facade over a tracer backend — usually **OpenTelemetry** or **Brave/Zipkin**. Add `micrometer-tracing-bridge-otel` (or `-brave`) plus an exporter. Boot then auto-instruments HTTP clients (`RestTemplate`, `WebClient`, `RestClient`, the Feign/OpenFeign integration) and server-side handling so the current context is **injected** into outbound requests and **extracted** from inbound ones automatically. You rarely touch headers yourself. **The W3C traceparent header.** The default format in Boot 3 is **W3C Trace Context**, a standard header named `traceparent`: ``` traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 ``` Four dash-separated fields: **version** (`00`); **trace-id** (32 lowercase hex = 16 bytes, all-zero is invalid); **parent-id** a.k.a. the caller's **span-id** (16 hex = 8 bytes) which becomes the callee's parent; **trace-flags** (2 hex) whose least-significant bit is the **sampled** flag (`01` = record/export this trace, `00` = don't). A companion `tracestate` header carries optional vendor-specific key/values and is propagated alongside. **What the receiver does.** The inbound server instrumentation reads `traceparent`, reuses the trace ID, sets its new span's parent to the incoming span ID, and honours the sampled flag so the whole trace samples consistently. IDs also flow into the logging **MDC**, so with the default Boot log pattern each line shows `[appName,traceId,spanId]` — letting you pivot from a log line to the full trace. **When there's no incoming header.** The first service in the chain (the trace root) has nothing to extract, so it *starts* a new trace: generates a fresh trace ID, makes a root span with no parent, and makes the sampling decision that everyone downstream inherits. **Gotchas.** (1) Propagation only happens across **instrumented** boundaries — a hand-rolled `HttpURLConnection` or a raw socket won't carry context unless you inject manually via the `Propagator` API. (2) If you break out of the request thread (manual `new Thread`, an un-instrumented executor) the context can be lost; use context-propagating executors or Micrometer's `ContextSnapshot`. (3) Sampling is all-or-nothing per trace via the flag — a downstream service should not independently flip a sampled=1 trace to unsampled.

  • If service A calls B calls C, do all three share one trace ID?
    Yes. The trace ID is generated once at the root (A if A has no inbound context) and copied unchanged along the chain via traceparent. Each hop only changes the parent/span ID, so B's parent is A's span and C's parent is B's span, but the 32-hex trace ID stays identical across all three.
  • What happens to the sampled flag if A decides not to sample?
    A sends traceparent flags `00`. B and C should honour that head-based decision and not export their spans either, keeping the trace consistent — you never have a half-recorded trace. Consistent propagation of the sampling bit is why head sampling works across services.

saying these in an interview costs you the question

  • Thinking each service invents its own trace ID (that would fragment the trace)
  • Believing you must manually set the traceparent header on every RestTemplate call
  • Confusing span ID and trace ID, or saying they're the same value
  • Not knowing propagation is only automatic across instrumented clients

context