skip to content

Distributed Tracing

Micrometer Tracing, the successor to Sleuth, creates spans and propagates trace ids across services through W3C or B3 headers, exporting to Zipkin or an OTel collector. Interviewers ask how you would follow one request across five services, and this is the answer.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What is distributed tracing, and what are trace IDs and span IDs in a Spring Boot application using Micrometer Tracing (formerly Spring Cloud Sleuth)?

level: juniorimportance: must knowfreq 70%

answer

  1. trace ID = whole request, span ID = one step
  2. Sleuth -> Micrometer Tracing (Boot 3)
  3. IDs pushed into MDC -> logs
  4. [appName,traceId,spanId] log pattern
  5. propagate + export to Zipkin

basics

~20 s

Distributed tracing follows one request as it hops between services. A trace ID is a shared id for the whole request; a span id marks one step (like one service call). Micrometer Tracing adds both to logs automatically.

solid answer

~40 s

Distributed tracing lets you follow a single request end-to-end as it crosses multiple services. Micrometer Tracing (the successor to Spring Cloud Sleuth) assigns each incoming request a trace ID that stays constant across every service it touches, and a span ID for each individual unit of work (an HTTP call, a DB query). Spans form a parent-child tree, so you can see who called whom and how long each step took. With the right dependencies on the classpath, Spring Boot auto-configures this: it injects trace and span IDs into your logs via MDC and can export the timing data to a backend like Zipkin. This turns a pile of disconnected logs across services into one correlated timeline you can search by trace ID.

code

java · 18 lines
java
// build.gradle dependencies (Spring Boot 3)
// implementation 'org.springframework.boot:spring-boot-starter-actuator'
// implementation 'io.micrometer:micrometer-tracing-bridge-brave'
// implementation 'io.zipkin.reporter2:zipkin-reporter-brave'

@RestController
class OrderController {
    private static final Logger log = LoggerFactory.getLogger(OrderController.class);

    @GetMapping("/orders/{id}")
    public String get(@PathVariable String id) {
        // No manual work: the trace ID and span ID are already in MDC,
        // so this log line is automatically tagged, e.g.:
        // [order-service,3f1a...,a2b9...] fetching order 42
        log.info("fetching order {}", id);
        return "order-" + id;
    }
}

go deeper

for a junior

Know the trace-ID-vs-span-ID distinction and that Boot auto-adds them to logs.

for a middle

Explain the parent-child span tree and how IDs propagate across HTTP calls.

for a senior

Discuss the Sleuth->Micrometer migration and the bridge/exporter dependency split.

for a principal

Frame tracing as one output of the unified Observation model and reason about org-wide correlation standards.

## The problem In a microservice system, one user action (say, 'place order') fans out into calls across many services: gateway -> order-service -> payment-service -> inventory-service. When something is slow or fails, each service has its own logs, and there is no obvious way to connect them. **Distributed tracing** solves this by tagging every hop of a single request with shared identifiers. ## Core vocabulary - **Trace**: the whole journey of one request through the system. Identified by a **trace ID** (a hex string) that is generated once at the entry point and then propagated unchanged to every downstream service. - **Span**: a single named, timed unit of work within a trace (e.g. 'handle HTTP GET /orders', 'SELECT from orders', 'call payment-service'). Each span has its own **span ID**, a start time, a duration, and a reference to its **parent span ID**. Spans form a tree/DAG describing causality. - **Trace context**: the small bundle of data (trace ID, current span ID, sampling decision) that must travel with the request so downstream services can continue the same trace. ## Micrometer Tracing vs Spring Cloud Sleuth **Spring Cloud Sleuth** was the old (Spring Boot 2.x) library that did this. As of Spring Boot 3, Sleuth is **discontinued and replaced by Micrometer Tracing**, which lives under the Micrometer project and integrates with the **Observation API** (`io.micrometer.observation`). You add Micrometer Tracing plus a **tracer bridge** (Brave or OpenTelemetry) and optionally an exporter (e.g. Zipkin). ## What Spring Boot auto-configures With the dependencies present, Spring Boot's `ObservationAutoConfiguration` and the tracing bridge auto-configuration wire up: 1. An **`ObservationRegistry`** and a **`Tracer`** bean. 2. Automatic instrumentation of common integration points: incoming server requests (`WebMvc`/`WebFlux`), outgoing `RestTemplate`/`WebClient` calls, scheduled tasks, messaging, etc. Each becomes an observation that opens a span. 3. **Log correlation**: trace ID and span ID are pushed into **SLF4J MDC** and appear in your log lines. The default Spring Boot log pattern shows `[appName,traceId,spanId]`. ## A concrete flow 1. A request arrives at the gateway with no trace context. The server instrumentation generates a new trace ID and a root span ID. 2. The gateway calls order-service via `WebClient`. The client instrumentation **injects** the trace context into outbound HTTP headers (W3C `traceparent` or B3 headers). 3. order-service's server instrumentation **extracts** those headers, so it continues the SAME trace ID but creates a new child span. 4. Every service reports its finished spans to a collector (Zipkin), which reassembles them into one waterfall view. ## When to use / not use Use tracing whenever requests cross process boundaries and you need to reason about latency or failures across services. Even in a single service it gives you log correlation per request. The main cost is a small per-request overhead and the need to run a trace backend, which you manage with **sampling** (only recording a fraction of traces). ## Gotchas for a beginner - Trace IDs propagate automatically only across **instrumented** integrations. Raw `java.net.HttpURLConnection` or a manually created thread may drop the context. - If you see IDs in logs but nothing in Zipkin, you likely have the tracing bridge but not the exporter dependency, or sampling is set to 0.

  • How does the trace ID end up in your log lines without you writing any code?
    The tracing instrumentation puts `traceId` and `spanId` into SLF4J's MDC when a span is current, and Spring Boot's default logging pattern includes `[applicationName,traceId,spanId]`, so every log emitted inside that span carries the IDs.
  • What replaced Spring Cloud Sleuth and why?
    Micrometer Tracing replaced it in Spring Boot 3. The instrumentation model was unified under the Micrometer Observation API so a single `Observation` produces both metrics and traces, and Sleuth's separate machinery was retired.

saying these in an interview costs you the question

  • Thinking each service generates its own separate trace ID (defeats correlation — the trace ID must be propagated unchanged).
  • Believing Sleuth is still the current Spring Boot 3 library.
  • Assuming trace IDs appear in Zipkin with only the bridge dependency and no exporter.

context

open as a page

How is trace context propagated between services, and what is the difference between W3C traceparent and B3 propagation formats in Micrometer Tracing?

level: seniorimportance: must knowfreq 60%

basics

~20 s

The trace ID, span ID, and sample flag travel in HTTP headers. The caller injects them, the callee extracts them. W3C uses one traceparent header; B3 (from Zipkin) uses multiple X-B3-* headers or a single b3 header. You pick the format via configuration.

open as a page

How does the Micrometer Observation API create spans, and how do you create a custom span in a Spring Boot 3 application?

level: middleimportance: should knowfreq 55%

basics

~20 s

You wrap code in an Observation using the ObservationRegistry. Each observation becomes a span (and a metric). Start it, run your code inside observe(), and Micrometer Tracing opens a span with the current trace context as parent.

open as a page

What is the role of the Brave vs OpenTelemetry bridge in Micrometer Tracing, and what dependencies do you need to export spans to Zipkin?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Micrometer Tracing defines a vendor-neutral API; a bridge plugs in a real tracer — either Brave or OpenTelemetry. To send spans to Zipkin you add the bridge plus a Zipkin reporter/exporter dependency; Spring Boot auto-configures the endpoint.

open as a page

How does trace context survive across thread boundaries (async, thread pools, reactive) in Micrometer Tracing, and how does sampling affect what you can observe?

level: principalimportance: should knowfreq 40%

basics

~20 s

Trace context is stored per-thread, so it does not automatically follow work handed to another thread. You must propagate it — wrap executors, use context-propagation instrumentation, or capture/restore the context. Sampling decides upfront whether a whole trace is recorded, so unsampled traces produce no spans.

open as a page