Distributed Tracing
When one user request fans out across five services, a stack trace in any single service tells you almost nothing. Distributed tracing stitches the whole journey together: each request gets a trace id that flows through every hop, and each unit of work gets a span. In Spring Boot 3 this is handled by Micrometer Tracing — the successor to the now-retired Spring Cloud Sleuth.
Traces, spans, and correlation ids
trace 7f3a... (one request, end to end)
├─ span A gateway [██████████████]
│ ├─ span B orders [████████]
│ │ ├─ span C inventory [███]
│ │ └─ span D payments [████]
| Term | Meaning |
|---|---|
| Trace id | Identifies one end-to-end request across all services |
| Span id | Identifies one unit of work (an HTTP call, a DB query) |
| Parent span | The span that caused this one — builds the tree |
| Correlation id | The trace/span ids injected into log lines |
Micrometer Tracing propagates these ids automatically across HTTP clients, server requests, and messaging — you write almost no tracing code.
Dependencies
Micrometer Tracing needs a bridge (the tracer implementation) and a reporter (where spans go). The common choice is the Brave bridge exporting to Zipkin:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>
Prefer OpenTelemetry? Swap the bridge and reporter:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-zipkin</artifactId>
</dependency>
| Bridge | Propagation | Typical backend |
|---|---|---|
micrometer-tracing-bridge-brave | B3 | Zipkin |
micrometer-tracing-bridge-otel | W3C traceparent | Tempo, Jaeger, any OTLP collector |
Configuration
management:
tracing:
sampling:
probability: 1.0 # sample 100% in dev; lower in production
zipkin:
tracing:
endpoint: http://localhost:9411/api/v2/spans
Run Zipkin locally to receive spans:
docker run -d -p 9411:9411 openzipkin/zipkin
Note: Sampling at
1.0is fine in development but costly at scale. In production sample a fraction (e.g.0.1) — Micrometer ensures a sampling decision is consistent across the whole trace.
What you get for free
With the dependencies present, Spring auto-instruments:
- Incoming HTTP requests (a server span per request).
RestClient,WebClient,RestTemplate, and@FeignClientcalls (propagating ids downstream).- Messaging via RabbitMQ/Kafka.
- Scheduled tasks and
@Asyncmethods.
So a request entering the gateway and flowing to orders → inventory shows up in Zipkin as one connected trace, no code changes.
Log correlation
The real day-to-day win is correlated logs. Micrometer puts the trace and span ids into the MDC; configure the log pattern to print them:
logging:
pattern:
level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"
Output (orders-service):
INFO [orders-service,7f3a9b2c1d4e5f60,a1b2c3d4e5f6a7b8] Placing order 42
Output (inventory-service, same request):
INFO [inventory-service,7f3a9b2c1d4e5f60,b8a7c6d5e4f3a2b1] Reserving sku COFFEE-1
Same traceId (7f3a...) across both services — grep your aggregated logs by trace id and you see the entire request, in order, across the fleet. See Logging Configuration for log setup.
Custom spans
For a meaningful business operation that isn’t an HTTP/DB call, create a span explicitly with the Observation API or the Tracer:
@Service
@RequiredArgsConstructor
public class PricingService {
private final ObservationRegistry registry;
public Money price(Cart cart) {
return Observation.createNotStarted("pricing.calculate", registry)
.observe(() -> expensiveCalculation(cart));
}
}
Tip: Prefer the
ObservationRegistryAPI over the low-levelTracer— one observation simultaneously produces a span and a timer metric, so tracing and metrics stay in sync.
Tracing pairs naturally with metrics and health from Actuator and Micrometer Metrics to complete the three pillars of observability.