Distributed Tracing With Spring Cloud Sleuth And Zipkin

While building complex applications with microservice architecture, a service flow may span between multiple services. To trace the request flow between multiple services, there are multiple tracing systems available. In this post, we will learn how to implement distributed tracing with the Zipkin tracing system in a spring boot sleuth starter library.

Zipkin is a distributed tracing system. It helps in gathering timing data and checks the latency issues between multiple services. Zipkin system contains Zipkin UI to display the tracing details on the web browser.

Zipkin uses a trace ID generated by the application to track the request. We need to log the trace ID in our application log so that the Zipkin system can process it. We can use the spring cloud sleuth library to generate the required trace ID details.

Version Details

  • Java version 1.8
  • Spring Boot : 2.2.5.RELEASE
  • Zipkin server 2.20.1-exec.jar

Table of Contents

Download the Zipkin server

We can download the executable Zipkin server jar file and run the jar file using the following command on the Windows system.

We can also run the Zipkin server as a Docker image.

Start the downloaded jar file by opening the command prompt and running the following command.

java -jar zipkin-server-xxx-exec.jar

Once the server is up and running, we can be able to view the default port it is running on. By default, the Zipkin server starts on port 9411.

zipkin server jar

We can access the Zipkin server console at http://localhost:9411.

zipkin server UI

Create Spring Boot services

Let us create two spring boot services to demonstrate how the Zipkin server can help us to trace the request, that spans across multiple services.

We will create two Spring boot applications. Fist service will make a GET API call using RestTemplate from one service to another.
Spring cloud sleuth dependency creates the required trace ID details for both of the services.

Create first Spring boot service

Create a simple Spring boot application with the name spring-boot-zipkin-example-service-1 with spring-cloud-starter-sleuth and spring-cloud-starter-zipkin dependencies. The spring cloud sleuth dependency creates the required span and trace ID details and adds those details in the generated application log.

The below list shows the added maven dependency details.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>

Spring cloud Zipkin dependency sends the generated trace details to the Zipkin server(To default URL: http://localhost:9411).

Add the following property to the application.properties file. The spring application name will be used in the log generated by the spring cloud sleuth.

spring.application.name=test1

Modify the Spring boot application starter java class with the below code.

package com.asbnotebook;

import java.net.URI;
import java.net.URISyntaxException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class SpringBootZipkinExampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootZipkinExampleApplication.class, args);
    }
    
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

@RestController
class ExampleController {

    @Autowired
    private RestTemplate restTemplate;

    private static final Log log = LogFactory.getLog(ExampleController.class);

    @GetMapping("/test")
    public String testZipkin() throws URISyntaxException {
        log.info("calling service!!");
        String message = restTemplate.getForObject(new URI("http://localhost:8081/test2"), String.class);
        log.info("message from service 2: " + message);
        return "working!!";
    }
}

We have created a GET endpoint /test, that invokes another service endpoint /test2, (that we are going to create in the next step) using RestTemplate.

Create the second service

Create another Spring boot application with the name spring-boot-zipkin-example-service-2 with spring-cloud-starter-sleuth and spring-cloud-starter-zipkin dependencies.

Add the Spring application name property to the application.properties file. Also, update the server port to 8081.

server.port=8081
spring.application.name=test2

Modify the Spring boot application starter java class with the following code.

package com.asbnotebook;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class SpringBootZipkinExampleService2Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootZipkinExampleService2Application.class, args);
    }
}

@RestController
class ExampleController2 {
    
    private static final Log log = LogFactory.getLog(ExampleController2.class);
            
    @GetMapping("/test2")
    public String testZipkin() {
        log.info("calling service 2!!");
        return "working 2!!";
    }
}

Time to run the application

Start the Spring boot applications.

We can access the first service endpoint at http://localhost:8080/test.

zipkin spring boot example

We should be able to view the logs along with generated trace Id and span ID details.

Log output of service 1

zipkin example spring boot

Log output of service 2

zipkin example spring boot

Now we should be able to view the tracing details of the request on the Zipkin UI.

Zipkin tracing example Spring boot

We can be able to view the tracing details by clicking on the request.

Zipkin server example spring boot

It also displays the latency details between each service call.

Zipkin spring boot example

Conclusion

In this article, we learned the basics of the Zipkin tracing system and implementation of distributed tracing with the help of Spring cloud sleuth library to generate trace IDs.

Zipkin trace details help us detect any latency issues that are slowing down the system by providing detailed tracing information across multiple services.

Further, we can aggregate the trace details into elastic search, Casandra, or MySQL database to preserve the tracing data.

Example code is available on Github.

Distributed Tracing With Spring Cloud Sleuth And Zipkin
Scroll to top

Discover more from ASB Notebook

Subscribe now to keep reading and get access to the full archive.

Continue reading