Last updated:
Zipkin with Spring Boot: Distributed Tracing for Spring Apps
What Are Spring Boot and Spring Cloud Sleuth?
The Spring Framework provides a programming and configuration model based on Java, which lets you build applications that can be deployed on any platform.
The Spring Boot project provides framework modules that let you quickly create applications and run them as standalone deployment units. It automatically configures Spring and third-party libraries during the build process. It also helps you build common patterns in distributed systems, such as service discovery, circuit breakers, micro-proxies, and distributed sessions.
Spring Boot contains a component called Spring Cloud Sleuth, which provides Spring Boot auto-configuration for distributed tracing. Combined with the open source Zipkin library, it allows you to perform distributed tracing for Spring applications.
This is part of a series of articles about Zipkin.
Spring Cloud Sleuth and Zipkin
Spring Cloud Sleuth, combined with Zipkin, allows you to perform distributed tracing for Spring applications.
Zipkin is a distributed tracking system originally developed by Twitter and now available as open source. Zipkin visualizes trace data between and within services. Its Java-enabled architecture consists of four components:
- Collector—validates received data and passes it to the storage service.
- Storage service—you can define any common database such as Cassandra, ElasticSearch, or MySQL.
- Search service—supports flexible queries on distributed trace data.
- Web UI—lets you visualize data and perform searches.
Spring Cloud Sleuth defines Spring Boot auto-configuration for distributed tracing. It automatically configures:
- Spans—defines how to break down trace data.
- Samples—defines how many traces to store.
- Remote fields—defines whether “baggage” should be sent to provide additional context about a trace.
- Libraries—defines which libraries should be traced.
Here are a few unique capabilities of Spring Cloud Sleuth:
- Adds Trace ID and Scope ID to Simple Logging Facade for Java with Mapped Diagnostic Context (SLF4J MDC). This lets you get all the logs from a specific trace or scope in the log collector.
- Measures common input and output points in Spring applications, including servlet filters, scheduled actions, REST templates, feign clients, and message channels.
- If
spring-cloud-sleuth-zipkinis available, Spring Cloud Sleuth generates and reports Zipkin-compatible traces via HTTP, sending them to a Zipkin collector service on port 9411 by default.
Example: Distributed Tracing with Spring Cloud Sleuth and Zipkin
Create a Spring Application
This tutorial uses Spring Web and Spring Cloud Sleuth for distributed tracing of a Spring application.
Set up a Spring application that uses Spring Web and Spring Cloud Sleuth:
- Add Spring Web and Spring Cloud Sleuth while creating an application.
- Use the following code to create a controller with two request mappings:
public class DemoController {
private static final demoLogger logger = demoLoggerFactory.getLogger(demoController.class);
private RestTemplate demoRestTemplate;
@Value("${spring.application.name}")
private String demoApplication;
public demoController(RestTemplate demoRestTemplate) {
this.demoRestTemplate = demoRestTemplate;
}
@GetMapping("/demoPath1")
public ResponseEntity demoPath1() {
logger.info("Request at {} for request /demoPath1 ", applicationName);
String requestResponse = demoRestTemplate.getForObject("http://localhost:8090/service/demoPath2”, String.class);
return ResponseEntity.ok("response from /demoPath1 + "+ response);
}
@GetMapping("/path2")
public ResponseEntity demoPath2(){
logger.info("Request at {} at /demoPath2", applicationName);
return ResponseEntity.ok("response from /demoPath2 ");Here, demoPath1 calls demoPath2 at a fixed port (8090). Two separate instances of the same application are running here.
Use the following code to inject RestTemplate as a bean and allow Spring Cloud Sleuth to add headers with trace id and span id in the outgoing request:
@Bean
public RestTemplate demoRestTemplate(RestTemplateBuilder builder) {
return builder.build();
}Run the following command in the terminal to build the application:
mvn clean verifyRun the following command to start one instance of the application:
java -jar \
target/Distributed-Service-0.0.1-SNAPSHOT.jar \
--spring.application.name=first-service-instance \
--server.port=8080In a separate terminal, run the following command to run the second instance:
java -jar \
target/Distributed-Service-0.0.1-SNAPSHOT.jar \
--spring.application.name=first-service-instance \
--server.port=8080Once both instances are running, use the following commands in separate terminals to call them:
curl -i http://localhost:8080/service/demoPath1This would show a log entry like below:
INFO [first-service-instance,222f3b00a283c75c,222f3b00a283c75c] 41114 --- [nio-8080-exec-1] c.a.p.distributedservice.Controller : Incoming request at first-service-instance for request /demoPath1The square brackets after INFO contain the instance name, trace id, and span id, respectively. They are the same for the first incoming request since there is no trace id.
curl -i http://localhost:8090/service/demoPath2This would show a log entry like below:
INFO [Service-2,222f3b00a283c75c,13194db963293a22] 41052 --- [nio-8090-exec-1] c.a.p.distributedservice.Controller : Incoming request at second-service-instance at /demoPath2The span id has changed here, but the trace id remains the same.
Visualize a Trace With Zipkin
This tutorial uses Zipkin to visualize the traces.
To integrate Zipkin into the application:
Add the following to application dependencies:
INFO [Service-2,222f3b00a283c75c,13194db963293a22] 41052 --- [nio-8090-exec-1] c.a.p.distributedservice.Controller : Incoming request at second-service-instance at /demoPath2Create a docker-compose file to start the Zipkin server:
version: '3.1'
services:
zipkin:
image: openzipkin/zipkin:2
ports:
- '9411:9411'In a separate terminal window, use the following command to start the server:
docker-compose upOpenTelemetry-Native Observability with Dash0
Dash0 is built on OpenTelemetry. Any application instrumented with an OpenTelemetry SDK, and any OpenTelemetry Collector, can send traces, metrics, and logs to Dash0 over OTLP. In Dash0 you can:
- Explore traces in a waterfall, a flame graph, or a graph of how a request flowed across services.
- Find what slow or failing requests have in common: Triage compares erroneous or slow spans with a baseline and highlights the attribute values that differ.
- Move between signals: The span sidebar shows the related logs and events for each span.
Because your telemetry stays in OpenTelemetry format, you can point the exporter at another backend without re-instrumenting.
