Implementing distributed tracing with Spring Cloud Sleuth and Zipkin
A request that crosses an API gateway, authentication service, order service and database can be difficult to diagnose from ordinary logs. Distributed tracing connects those separate operations into one trace, showing where time is spent and which service returned an error.
Spring Cloud Sleuth adds trace and span identifiers to Spring applications, while Zipkin collects and displays the resulting telemetry. Together, they provide a practical way to follow a transaction through microservices without manually matching timestamps across several log files.
The examples below use the Sleuth generation commonly paired with Spring Boot 2.6 or 2.7. Spring Boot 3 applications should use Micrometer Tracing instead, because Sleuth is no longer the preferred Spring Cloud solution. This distinction matters when maintaining systems for Australian retailers, banks or government suppliers with long support cycles.
| Spring Boot generation | Tracing library | Zipkin integration |
|---|---|---|
| 2.6–2.7 | Spring Cloud Sleuth | spring-cloud-sleuth-zipkin |
| 3.x and later | Micrometer Tracing | Brave bridge and Zipkin reporter |
| Any version | Zipkin server | HTTP endpoint, commonly port 9411 |
How trace context works
A trace represents the complete journey of one request. Each operation within that journey is a span, such as an HTTP call, repository query or message-consumer action. The trace ID remains consistent, while every span receives its own span ID and timing information.
When Service A calls Service B, Sleuth propagates the context through HTTP headers. The receiving service extracts those headers and continues the same trace. This lets a developer inspect a customer checkout request across services running in Sydney, Melbourne or another region.
For production systems, sample requests rather than recording everything by default. A sampling rate of 0.1 records roughly ten per cent of traces, reducing storage and network overhead while retaining useful diagnostic coverage.
Adding Sleuth to a Spring Boot service
Use the Spring Cloud BOM so that Sleuth and Spring Boot dependencies remain compatible. A typical Maven configuration looks like this:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2021.0.8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
</dependencies>
Sleuth instruments Spring MVC, WebClient, RestTemplate and common messaging integrations. For custom executors, use Spring-managed task execution or decorate the executor so trace context is not lost when work moves to another thread.
Connecting the application to Zipkin
Zipkin can run locally with Docker:
docker run -d --name zipkin -p 9411:9411 openzipkin/zipkin
Configure the service in application.yml:
spring:
application:
name: payment-service
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1.0
The 1.0 value is useful during development because every request is recorded. In production, a lower value such as 0.05 is usually safer. If Zipkin is hosted in a separate environment, use its internal service address rather than a public URL, and secure communication with TLS where appropriate.
Reading trace data and logs
Start Zipkin at http://localhost:9411, choose a service name and inspect a trace. The dependency graph shows which services participated, while the timeline reveals slow network calls, database operations and retries. A long child span often identifies the real bottleneck more accurately than the overall request duration.
Sleuth also enriches log entries with values similar to:
INFO [payment-service,64f2c9a1d7e8b301,8a91c53f2a1d44c90] Payment approved
The first value is the application name, followed by the trace ID and span ID. Include these fields in structured JSON logs so a platform such as Elasticsearch, CloudWatch or an Australian-hosted observability service can correlate logs with Zipkin records.
Propagating context across custom calls
Automatic instrumentation works for supported clients, but manually constructed asynchronous or integration code can break propagation. Prefer an injected RestTemplate or WebClient.Builder rather than creating clients with new, because Sleuth can then apply the required interceptors.
For a custom span, use Sleuth’s tracer:
@Autowired
private Tracer tracer;
public void publishAuditEvent() {
Span span = tracer.nextSpan().name("publish-audit-event").start();
try (Tracer.SpanInScope scope = tracer.withSpan(span)) {
// Publish the event
} finally {
span.end();
}
}
Avoid placing personal information, payment details or full request bodies in span tags. Australian organisations frequently need careful handling of health, financial and identity data, especially when telemetry is stored outside the country.
Moving from Sleuth to current tracing
For a Boot 3 migration, replace Sleuth with Micrometer Tracing dependencies and a Brave Zipkin reporter. The concepts remain the same: traces, spans, propagation, sampling and service names. Configuration names and APIs change, so test HTTP, messaging and scheduled-job flows rather than assuming the migration is transparent.
Tracing is most valuable when paired with sensible service boundaries, correlation-aware logs and useful metrics. If a legacy platform spans Perth, Brisbane and an external cloud region, latency may reflect network distance rather than application code. A Java specialist can review instrumentation and deployment boundaries through IT consultancy services, particularly when reliability, compliance and observability need to work together.
Use Zipkin to investigate real user journeys, such as an online order placed during a busy Sydney sales event or a payment request affected by a downstream timeout. With consistent propagation and restrained sampling, distributed tracing turns a vague “the API is slow” report into a traceable sequence of service operations.