Search Tutorials


Spring Cloud Tutorial - Distributed Log Tracing using Sleuth and Zipkin Example | JavaInUse

Spring Cloud Tutorial - Distributed Log Tracing using Sleuth and Zipkin Example

Microservices architecture involve multiple services which interact with each other. So a functionality may involve call to multiple microservices. Usually for systems developed using Microservices architecture, there are many microservices involved. These microservices collaborate with each other.
Consider the following microservices-
sprcloud_6-1
If suppose during such calls there are some issues like exception has occurred. Or may be there are latency issues due to a particular service taking more than expected time. How do we identify where the issue is occurring. In regular project we would have used logging to analyze the logs to know more about occurred exceptions and also performance timing. But since microservices involves multiple services we cannot use regular logging. Each Service will be having its own separate logs. So we will need to go through the logs of each service. Also how do we correlate the logs to a request call chain i.e which logs of microservices are related to Request1, which are related to Request2. To resolve these issues we make use of Spring Cloud Sleuth and Zipkin
  • Spring Cloud Sleuth is used to generate and attach the trace id, span id to the logs so that these can then be used by tools like Zipkin and ELK for storage and analysis
  • Zipkin is a distributed tracing system. It helps gather timing data needed to troubleshoot latency problems in service architectures. Features include both the collection and lookup of this data.

Spring Cloud - Table Of Contents

Microservice Registration and Discovery with Spring cloud using Netflix Eureka- Part 1. Microservice Registration and Discovery with Spring cloud using Netflix Eureka - Part 2. Microservice Registration and Discovery with Spring cloud using Netflix Eureka - Part 3. Microservice Registration and Discovery with Spring cloud using Netflix Eureka - Part 4. Spring Cloud- Netflix Eureka + Ribbon Simple Example Spring Cloud- Netflix Eureka + Ribbon + Hystrix Fallback Simple Example Spring Cloud- Netflix Hystrix Circuit Breaker Simple Example Spring Cloud- Netflix Feign REST Client Simple Example Spring Cloud- Netflix Zuul +Eureka Simple Example Spring Cloud Config Server using Native Mode Simple Example Spring Cloud Config Server Using Git Simple Example Spring Boot Admin Simple Example Spring Cloud Stream Tutorial - Publish Message to RabbitMQ Simple Example Spring Cloud Stream Tutorial - Consume Message from RabbitMQ Simple Example Spring Cloud Tutorial - Publish Events Using Spring Cloud Bus Spring Cloud Tutorial - Stream Processing Using Spring Cloud Data Flow Spring Cloud Tutorial - Distributed Log Tracing using Sleuth and Zipkin Example Spring Cloud Tutorial - Spring Cloud Gateway Hello World Example Spring Cloud Tutorial - Spring Cloud Gateway Filters Example Spring Cloud Tutorial - Spring Cloud Gateway + Netflix Eureka Example Spring Cloud Tutorial - Spring Cloud Gateway + Netflix Eureka + Netflix Hystrix Example Spring Cloud Tutorial - Secure Secrets using Spring Cloud Config + Vault Example

Video

This tutorial is explained in the below Youtube Video.

Lets Begin-

We will be dividing this tutorial into 3 parts-
  • 1. Develop four Spring Boot Microservices modules which interact with each other.
  • 2. Implement distributed tracing using Spring Cloud Sleuth
  • 3. View distributed tracing using Zipkin

Develop four Spring Boot Microservices modules which interact with each other

We will be developing the spring boot microservices as follows-
Spring Cloud Sleuth Tutorial
The Spring Microservices will be making use of Spring RestTemplate for making REST calls
  • Microservice-1

    The maven project will be as follows-
    Spring Cloud Sleuth Module1
    The pom.xml will be as follows-
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
    
    		
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>com.javainuse</groupId>
    <artifactId>microservice1</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    
    <name>microservice1</name>
    
    <parent>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-parent</artifactId>
    	<version>2.1.7.RELEASE</version>
    	<relativePath /> <!-- lookup parent from repository -->
    </parent>
    
    <properties>
    	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    	<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    	<java.version>1.8</java.version>
    </properties>
    
    <dependencies>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-web</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-log4j2</artifactId>
    	</dependency>
    
    </dependencies>
    <build>
    
    	<plugins>
    		<plugin>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-maven-plugin</artifactId>
    		</plugin>
    	</plugins>
    </build>
    
    </project>
    
    Define the application.properties as follows-
    server.port=8080
    spring.application.name=microservice1
    



Create a class named Microservice1Application. In this class we -
  • Annotate with SpringBootApplication
  • Expose a GET REST API with mapping /microservice1. Also using RestTemplate we make a call to the second microservice. The response received from the microservice 2 is returned back to caller.
package com.javainuse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.http.HttpMethod;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class Microservice1Application {

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

@RestController
class Microservice1Controller {

	private final Logger LOG = LoggerFactory.getLogger(this.getClass());

	@Autowired
	RestTemplate restTemplate;

	@Bean
	public RestTemplate getRestTemplate() {
		return new RestTemplate();
	}

	@GetMapping(value = "/microservice1")
	public String method1() {
		LOG.info("Inside method1");
		String baseUrl = "http://localhost:8081/microservice2";
		String response = (String) restTemplate.exchange(baseUrl, HttpMethod.GET, null, String.class).getBody();
		LOG.info("The response received by method1 is " + response);
		return response;
	}
}
       
  • Microservice-2

    The maven project will be as follows-
    Spring Cloud Sleuth Module2
    The pom.xml will be as follows-
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
    
    		
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>com.javainuse</groupId>
    <artifactId>microservice2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    
    <name>microservice2</name>
    
    <parent>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-parent</artifactId>
    	<version>2.1.7.RELEASE</version>
    	<relativePath /> <!-- lookup parent from repository -->
    </parent>
    
    <properties>
    	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    	<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    	<java.version>1.8</java.version>
    </properties>
    
    <dependencies>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-web</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-log4j2</artifactId>
    	</dependency>
    
    </dependencies>
    
    <build>
    	<plugins>
    		<plugin>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-maven-plugin</artifactId>
    		</plugin>
    	</plugins>
    </build>
    
    </project>
    
    Define the application.properties as follows-
    server.port=8081
    spring.application.name=microservice2
    
    Create a class named Microservice1Application. In this class we -
    • Annotate with SpringBootApplication
    • Expose a GET REST API with mapping /microservice3. Also using RestTemplate we make a call to the second microservice. The response received from the microservice 3 is returned back to caller.
    package com.javainuse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    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.http.HttpMethod;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    public class Microservice2Application {
    
    	public static void main(String[] args) {
    		SpringApplication.run(Microservice2Application.class, args);
    	}
    }
    
    @RestController
    class Microservice2Controller {
    
    	private final Logger LOG = LoggerFactory.getLogger(this.getClass());
    
    	@Autowired
    	RestTemplate restTemplate;
    
    	@Bean
    	public RestTemplate getRestTemplate() {
    		return new RestTemplate();
    	}
    
    	@GetMapping(value = "/microservice2")
    	public String method2() {
    		LOG.info("Inside method2");
    		String baseUrl = "http://localhost:8082/microservice3";
    		String response = (String) restTemplate.exchange(baseUrl, HttpMethod.GET, null, String.class).getBody();
    		LOG.info("The response received by method2 is " + response);
    		return response;
    	}
    }
    
  • Microservice-3

    The Maven Project will be as follows-
    Spring Cloud Sleuth Module3
    The pom.xml will be as follows-
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>com.javainuse</groupId>
    <artifactId>microservice3</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    
    <name>microservice3</name>
    
    <parent>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-parent</artifactId>
    	<version>2.1.7.RELEASE</version>
    	<relativePath /> <!-- lookup parent from repository -->
    </parent>
    
    <properties>
    	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    	<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    	<java.version>1.8</java.version>
    </properties>
    
    <dependencies>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-web</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-log4j2</artifactId>
    	</dependency>
    
    </dependencies>
    
    <build>
    	<plugins>
    		<plugin>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-maven-plugin</artifactId>
    		</plugin>
    	</plugins>
    </build>
    
    </project>
    
    Define the application.properties as follows-
    server.port=8082
    spring.application.name=microservice3
    
    Create a class named Microservice1Application3. In this class we -
    • Annotate with SpringBootApplication
    • Expose a GET REST API with mapping /microservice4. Also using RestTemplate we make a call to the second microservice. The response received from the microservice 4 is returned back to caller.
    package com.javainuse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    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.http.HttpMethod;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    public class Microservice3Application {
    
    	public static void main(String[] args) {
    		SpringApplication.run(Microservice3Application.class, args);
    	}
    }
    
    @RestController
    class Microservice3Controller {
    
    	private final Logger LOG = LoggerFactory.getLogger(this.getClass());
    
    	@Autowired
    	RestTemplate restTemplate;
    
    	@Bean
    	public RestTemplate getRestTemplate() {
    		return new RestTemplate();
    	}
    
    	@GetMapping(value = "/microservice3")
    	public String method3() {
    		LOG.info("Inside method3");
    		String baseUrl = "http://localhost:8083/microservice4";
    		try {
    			Thread.sleep(1000);
    		} catch (Exception ex) {
    
    		}
    		String response = (String) restTemplate.exchange(baseUrl, HttpMethod.GET, null, String.class).getBody();
    		LOG.info("The response received by method3 is " + response);
    		return response;
    	}
    }
    
  • Microservice-4

    The Maven Project will be as follows-
    Spring Cloud Sleuth Module4
    The pom.xml will be as follows-
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>com.javainuse</groupId>
    <artifactId>microservice4</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    
    <name>microservice4</name>
    
    <parent>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-parent</artifactId>
    	<version>2.1.7.RELEASE</version>
    	<relativePath /> <!-- lookup parent from repository -->
    </parent>
    
    <properties>
    	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    	<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    	<java.version>1.8</java.version>
    </properties>
    
    <dependencies>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-web</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-log4j2</artifactId>
    	</dependency>
    
    </dependencies>
    
    <build>
    	<plugins>
    		<plugin>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-maven-plugin</artifactId>
    		</plugin>
    	</plugins>
    </build>
    
    </project>
    
    Define the application.properties as follows-
    server.port=8083
    spring.application.name=microservice4
    
    Create a class named Microservice1Application. In this class we -
    • Annotate with SpringBootApplication
    • Expose a GET REST API with mapping /microservice4 which returns a String response.
    package com.javainuse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    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 Microservice4Application {
    
    	public static void main(String[] args) {
    		SpringApplication.run(Microservice4Application.class, args);
    	}
    }
    
    @RestController
    class Microservice4Controller {
    
    	private final Logger LOG = LoggerFactory.getLogger(this.getClass());
    
    	@Autowired
    	RestTemplate restTemplate;
    
    	@Bean
    	public RestTemplate getRestTemplate() {
    		return new RestTemplate();
    	}
    
    	@GetMapping(value = "/microservice4")
    	public String method4() {
    		LOG.info("Inside method4");
    		return "Hello World JavaInUse";
    	}
    }
           
  • Start all the microservices and hit the url -localhost:8080/microservice1
    Spring Cloud Output
    • Microservice-1 logs -
      Spring Cloud Sleuth Module1 output
    • Microservice-2 logs -
      Spring Cloud Sleuth Module2 output
    • Microservice-3 logs -
      Spring Cloud Sleuth Module3 output
    • Microservice-4 logs -
      Spring Cloud Sleuth Module4 output

    Implement distributed tracing using Spring Cloud Sleuth

    Next we will be adding the spring cloud sleuth for all the microservices. Using Spring Cloud Sleuth we will be adding a unique token to all requests. Spring Cloud Sleuth is used to generate and attach the trace id, span id to the logs so that these can then be used by tools like Zipkin and ELK for storage and analysis.
    Spring Cloud Zipkin Tutorial
    The Spring Cloud Sleuth Token has following components
    Spring Cloud Sleuth Token
    • Microservice-1

      The pom.xml will be as follows-
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      
      	<modelVersion>4.0.0</modelVersion>
      
      	<groupId>com.javainuse</groupId>
      	<artifactId>microservice1</artifactId>
      	<version>0.0.1-SNAPSHOT</version>
      	<packaging>jar</packaging>
      
      	<name>microservice1</name>
      
      	<parent>
      		<groupId>org.springframework.boot</groupId>
      		<artifactId>spring-boot-starter-parent</artifactId>
      		<version>2.1.7.RELEASE</version>
      		<relativePath /> <!-- lookup parent from repository -->
      	</parent>
      
      	<properties>
      		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      		<java.version>1.8</java.version>
      		<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
      	</properties>
      
      
      
      	<dependencies>
      		<dependency>
      			<groupId>org.springframework.cloud</groupId>
      			<artifactId>spring-cloud-starter-sleuth</artifactId>
      		</dependency>
      		<dependency>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-starter-web</artifactId>
      		</dependency>
      		<dependency>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-starter-log4j2</artifactId>
      		</dependency>
      
      	</dependencies>
      
      	<dependencyManagement>
      		<dependencies>
      			<dependency>
      				<groupId>org.springframework.cloud</groupId>
      				<artifactId>spring-cloud-dependencies</artifactId>
      				<version>${spring-cloud.version}</version>
      				<type>pom</type>
      				<scope>import</scope>
      			</dependency>
      		</dependencies>
      	</dependencyManagement>
      
      	<build>
      		<plugins>
      			<plugin>
      				<groupId>org.springframework.boot</groupId>
      				<artifactId>spring-boot-maven-plugin</artifactId>
      			</plugin>
      		</plugins>
      	</build>
      
      
      </project>
      
             
      Define the application.properties as follows-
      server.port=8080
      spring.application.name=microservice1
      spring.zipkin.enabled=false
      
      In the Microservice1Application class -
      • In distributed tracing the data volumes can be very high so sampling can be important.This determines what amount of data you want to send to a centralized log analysis tool. If you want to send all the data or only a part of it. If you are exporting span data to Zipkin or Spring Cloud Stream, there is also an AlwaysSampler that exports everything and a PercentageBasedSampler that samples a fixed fraction of spans. We will be making use of the Always Sampler for this example.
      package com.javainuse;
      
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      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.http.HttpMethod;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RestController;
      import org.springframework.web.client.RestTemplate;
      import brave.sampler.Sampler;
      
      @SpringBootApplication
      public class Microservice1Application {
      
      	public static void main(String[] args) {
      		SpringApplication.run(Microservice1Application.class, args);
      	}
      }
      
      @RestController
      class Microservice1Controller {
      
      	private final Logger LOG = LoggerFactory.getLogger(this.getClass());
      
      	@Autowired
      	RestTemplate restTemplate;
      
      	@Bean
      	public RestTemplate getRestTemplate() {
      		return new RestTemplate();
      	}
      
      	@Bean
      	public Sampler defaultSampler() {
      		return Sampler.ALWAYS_SAMPLE;
      	}
      
      	@GetMapping(value = "/microservice1")
      	public String method1() {
      		LOG.info("Inside method1");
      		String baseUrl = "http://localhost:8081/microservice2";
      		String response = (String) restTemplate.exchange(baseUrl, HttpMethod.GET, null, String.class).getBody();
      		LOG.info("The response received by method1 is " + response);
      		return response;
      	}
      }
             
    • Microservice-2

      The pom.xml will be as follows-
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      
      	<modelVersion>4.0.0</modelVersion>
      
      	<groupId>com.javainuse</groupId>
      	<artifactId>microservice2</artifactId>
      	<version>0.0.1-SNAPSHOT</version>
      	<packaging>jar</packaging>
      
      	<name>microservice2</name>
      
      	<parent>
      		<groupId>org.springframework.boot</groupId>
      		<artifactId>spring-boot-starter-parent</artifactId>
      		<version>2.1.7.RELEASE</version>
      		<relativePath /> <!-- lookup parent from repository -->
      	</parent>
      
      	<properties>
      		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      		<java.version>1.8</java.version>
      		<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
      	</properties>
      
      
      
      	<dependencies>
      		<dependency>
      			<groupId>org.springframework.cloud</groupId>
      			<artifactId>spring-cloud-starter-sleuth</artifactId>
      		</dependency>
      		<dependency>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-starter-web</artifactId>
      		</dependency>
      		<dependency>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-starter-log4j2</artifactId>
      		</dependency>
      
      	</dependencies>
      
      	<dependencyManagement>
      		<dependencies>
      			<dependency>
      				<groupId>org.springframework.cloud</groupId>
      				<artifactId>spring-cloud-dependencies</artifactId>
      				<version>${spring-cloud.version}</version>
      				<type>pom</type>
      				<scope>import</scope>
      			</dependency>
      		</dependencies>
      	</dependencyManagement>
      
      	<build>
      		<plugins>
      			<plugin>
      				<groupId>org.springframework.boot</groupId>
      				<artifactId>spring-boot-maven-plugin</artifactId>
      			</plugin>
      		</plugins>
      	</build>
      
      
      </project>
      
      Define the application.properties as follows-
      server.port=8081
      spring.application.name=microservice2
      spring.zipkin.enabled=false
             
      In the Microservice2Application class -
      • In distributed tracing the data volumes can be very high so sampling can be important.This determines what amount of data you want to send to a centralized log analysis tool. If you want to send all the data or only a part of it. If you are exporting span data to Zipkin or Spring Cloud Stream, there is also an AlwaysSampler that exports everything and a PercentageBasedSampler that samples a fixed fraction of spans. We will be making use of the AlwaysSampler for this example.
      package com.javainuse;
      
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      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.http.HttpMethod;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RestController;
      import org.springframework.web.client.RestTemplate;
      import brave.sampler.Sampler;
      
      @SpringBootApplication
      public class Microservice2Application {
      
      	public static void main(String[] args) {
      		SpringApplication.run(Microservice2Application.class, args);
      	}
      }
      
      @RestController
      class Microservice2Controller {
      
      	private final Logger LOG = LoggerFactory.getLogger(this.getClass());
      
      	@Autowired
      	RestTemplate restTemplate;
      
      	@Bean
      	public RestTemplate getRestTemplate() {
      		return new RestTemplate();
      	}
      
      	@Bean
      	public Sampler defaultSampler() {
      		return Sampler.ALWAYS_SAMPLE;
      	}
      
      	@GetMapping(value = "/microservice2")
      	public String method2() {
      		LOG.info("Inside method2");
      		String baseUrl = "http://localhost:8082/microservice3";
      		String response = (String) restTemplate.exchange(baseUrl, HttpMethod.GET, null, String.class).getBody();
      		LOG.info("The response received by method2 is " + response);
      		return response;
      	}
      }
             
    • Microservice-3

      The pom.xml will be as follows-
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      
      	<modelVersion>4.0.0</modelVersion>
      
      	<groupId>com.javainuse</groupId>
      	<artifactId>microservice3</artifactId>
      	<version>0.0.1-SNAPSHOT</version>
      	<packaging>jar</packaging>
      
      	<name>microservice3</name>
      
      	<parent>
      		<groupId>org.springframework.boot</groupId>
      		<artifactId>spring-boot-starter-parent</artifactId>
      		<version>2.1.7.RELEASE</version>
      		<relativePath /> <!-- lookup parent from repository -->
      	</parent>
      
      	<properties>
      		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      		<java.version>1.8</java.version>
      		<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
      	</properties>
      
      
      
      	<dependencies>
      		<dependency>
      			<groupId>org.springframework.cloud</groupId>
      			<artifactId>spring-cloud-starter-sleuth</artifactId>
      		</dependency>
      		<dependency>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-starter-web</artifactId>
      		</dependency>
      		<dependency>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-starter-log4j2</artifactId>
      		</dependency>
      
      	</dependencies>
      
      	<dependencyManagement>
      		<dependencies>
      			<dependency>
      				<groupId>org.springframework.cloud</groupId>
      				<artifactId>spring-cloud-dependencies</artifactId>
      				<version>${spring-cloud.version}</version>
      				<type>pom</type>
      				<scope>import</scope>
      			</dependency>
      		</dependencies>
      	</dependencyManagement>
      
      	<build>
      		<plugins>
      			<plugin>
      				<groupId>org.springframework.boot</groupId>
      				<artifactId>spring-boot-maven-plugin</artifactId>
      			</plugin>
      		</plugins>
      	</build>
      
      
      </project>
      
             
      Define the application.properties as follows-
      server.port=8082
      spring.application.name=microservice3
      spring.zipkin.enabled=false
             
      In the Microservice3Application class -
      • In distributed tracing the data volumes can be very high so sampling can be important.This determines what amount of data you want to send to a centralized log analysis tool. If you want to send all the data or only a part of it. If you are exporting span data to Zipkin or Spring Cloud Stream, there is also an AlwaysSampler that exports everything and a PercentageBasedSampler that samples a fixed fraction of spans. We will be making use of the AlwaysSampler for this example.
      package com.javainuse;
      
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      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.http.HttpMethod;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RestController;
      import org.springframework.web.client.RestTemplate;
      import brave.sampler.Sampler;
      
      @SpringBootApplication
      public class Microservice3Application {
      
      	public static void main(String[] args) {
      		SpringApplication.run(Microservice3Application.class, args);
      	}
      }
      
      @RestController
      class Microservice3Controller {
      
      	private final Logger LOG = LoggerFactory.getLogger(this.getClass());
      
      	@Autowired
      	RestTemplate restTemplate;
      
      	@Bean
      	public RestTemplate getRestTemplate() {
      		return new RestTemplate();
      	}
      
      	@Bean
      	public Sampler defaultSampler() {
      		return Sampler.ALWAYS_SAMPLE;
      	}
      
      	@GetMapping(value = "/microservice3")
      	public String method3() {
      		LOG.info("Inside method3");
      		String baseUrl = "http://localhost:8083/microservice4";
      		try {
      			Thread.sleep(1000);
      		} catch (Exception ex) {
      
      		}
      		String response = (String) restTemplate.exchange(baseUrl, HttpMethod.GET, null, String.class).getBody();
      		LOG.info("The response received by method3 is " + response);
      		return response;
      	}
      }
             
    • Microservice-4

      The pom.xml will be as follows-
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      
      	<modelVersion>4.0.0</modelVersion>
      
      	<groupId>com.javainuse</groupId>
      	<artifactId>microservice4</artifactId>
      	<version>0.0.1-SNAPSHOT</version>
      	<packaging>jar</packaging>
      
      	<name>microservice4</name>
      
      	<parent>
      		<groupId>org.springframework.boot</groupId>
      		<artifactId>spring-boot-starter-parent</artifactId>
      		<version>2.1.7.RELEASE</version>
      		<relativePath /> <!-- lookup parent from repository -->
      	</parent>
      
      	<properties>
      		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      		<java.version>1.8</java.version>
      		<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
      	</properties>
      
      
      
      	<dependencies>
      		<dependency>
      			<groupId>org.springframework.cloud</groupId>
      			<artifactId>spring-cloud-starter-sleuth</artifactId>
      		</dependency>
      		<dependency>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-starter-web</artifactId>
      		</dependency>
      		<dependency>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-starter-log4j2</artifactId>
      		</dependency>
      
      	</dependencies>
      
      	<dependencyManagement>
      		<dependencies>
      			<dependency>
      				<groupId>org.springframework.cloud</groupId>
      				<artifactId>spring-cloud-dependencies</artifactId>
      				<version>${spring-cloud.version}</version>
      				<type>pom</type>
      				<scope>import</scope>
      			</dependency>
      		</dependencies>
      	</dependencyManagement>
      
      	<build>
      		<plugins>
      			<plugin>
      				<groupId>org.springframework.boot</groupId>
      				<artifactId>spring-boot-maven-plugin</artifactId>
      			</plugin>
      		</plugins>
      	</build>
      
      
      </project>
      
      
             
      Define the application.properties as follows-
      server.port=8083
      spring.application.name=microservice4
      spring.zipkin.enabled=false
             
      In the Microservice4Application class -
      • Create the Sampler Bean.
      package com.javainuse;
      
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      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;
      import brave.sampler.Sampler;
      
      @SpringBootApplication
      public class Microservice4Application {
      
      	public static void main(String[] args) {
      		SpringApplication.run(Microservice4Application.class, args);
      	}
      }
      
      @RestController
      class Microservice4Controller {
      
      	private final Logger LOG = LoggerFactory.getLogger(this.getClass());
      
      	@Autowired
      	RestTemplate restTemplate;
      
      	@Bean
      	public RestTemplate getRestTemplate() {
      		return new RestTemplate();
      	}
      
      	@Bean
      	public Sampler defaultSampler() {
      		return Sampler.ALWAYS_SAMPLE;
      	}
      
      	@GetMapping(value = "/microservice4")
      	public String method4() {
      		LOG.info("Inside method4");
      		return "Hello World JavaInUse";
      	}
      }
             
    Start all the microservices and hit the url -localhost:8080/microservice1
    Spring Cloud Output
    • Microservice-1 logs-
      Spring Cloud Sleuth log1 output
    • Microservice-2 logs-
      Spring Cloud Sleuth log2 output
    • Microservice-3 logs-
      Spring Cloud Sleuth log3 output
    • Microservice-4 logs-
      Spring Cloud Sleuth log4 output

    Use Zipkin for distributed log analysis-

    • Zipkin is a distributed tracing system. It helps gather timing data needed to troubleshoot latency problems in service architectures. Features include both the collection and lookup of this data.
      Spring Cloud Sleuth + Zipkin Tutorial
      From maven repository get the latest zipkin jar. Once you have downloaded this jar using the command prompt run the execute jar command as follows-
      Spring Cloud - execute zipkin jar
      If we now go to the url - localhost:9411 we see the zipkin dashboard as follows-
      Spring Cloud - zipkin dashboard
    • In all the pom.xml add the spring cloud zipkin dependency as follows-
       <dependency>
      	<groupId>org.springframework.cloud</groupId>
      	<artifactId>spring-cloud-starter-zipkin</artifactId>
      </dependency>
       
      Also from the application.properties remove the property we had defined earlier - spring.zipkin.enabled=false
      Start the microservices. And again hit the url localhost:8080/microservice1
      Spring Cloud Output
    • Now go the zipkin dashboard and click on search logs-
      Spring Cloud - Zipkin distributed log tracing
      Here we can see the log stacktrace for the particular request chain. We can further analyze this request chain by selecting it in the dashboard.
      Spring Cloud - Zipkin distributed logs

    Download Source Code

    Download it -
    Spring Cloud Sleuth + Zipkin Microservices

    See Also

    Spring Boot Hello World Application- Create simple controller and jsp view using Maven Spring Boot Tutorial-Spring Data JPA Spring Boot + Simple Security Configuration Pagination using Spring Boot Simple Example Spring Boot + ActiveMQ Hello world Example Spring Boot + Swagger Example Hello World Example Spring Boot + Swagger- Understanding the various Swagger Annotations Spring Boot Main Menu Spring Boot Interview Questions