Let’s do some tracing. Tracing is an essential part of a microservice architecture. You need to be able to examine an event lifecycle. In a microservice architecture and event will flow through many microservices, transforming, forking, and merging. In a monolith, we can log the events in one log file and see everything that is going on. In a microservice architecture, we need a way to combine the logs from multiple microservices and tie together the log messages generated by a unique event. Enter Spring Cloud Sleuth. Spring Cloud Sleuth is an abstraction over a tracing library. In this example, we will use Brave as the underlying tracing library and Zipkin to record the events. Let’s get started.
Run this docker command
“bash docker-compose up —remove-orphans
`
Open a browser to http://localhost:9411/
# Create a sample Service and WebApp to generate traces and spans.
## Sample Service
Head over to https://start.spring.io/ and add the dependency **Spring Web**, **Spring Boot DevTools**, **Lombok**, **Slueth**, and **Zipkin Client**. Choose a Maven project type and use the Spring Boot version 2.3.3 and call the artifact 'service'. Import the project into SpringToolSuite or your favorite IDE.
Edit the application properties.
> src/main/resources/application.properties
```shell
server.port=8081
spring.application.name=sample-service
Add a simple controller and use annotations to create a span.
src/main/java/com/example/service/RootController.java
package com.example.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.sleuth.annotation.NewSpan;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Controller
public class RootController {
@Autowired
HelloService helloService;
@GetMapping("/")
@NewSpan("RootController#helloWorld")
@ResponseBody
public String helloWorld() {
log.info("Handling home");
return helloService.createMessage();
}
}
Add a simple service and use both annotations and explicit calls to create spans.
src/main/java/com/example/service/HelloService.java
package com.example.service;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.sleuth.annotation.NewSpan;
import org.springframework.stereotype.Service;
import brave.ScopedSpan;
import brave.Tracer;
@Service
public class HelloService {
@Autowired
private Tracer tracer;
SimpleDateFormat timeFormater = new SimpleDateFormat("hh:mm:ss");
@NewSpan("HelloService#createMessage with explicit span")
public String createMessage() {
ScopedSpan span = tracer.startScopedSpan("Add date to hello world");
String message = "Hello World at " + timeFormater.format(new Date());
span.finish();
return message;
}
}
Head over to https://start.spring.io/ and add the dependency Spring Web, Spring Boot DevTools, Slueth, and Zipkin Client. Choose a Maven project type and use the Spring Boot version 2.3.3 and call the artifact ‘webapp’. Import the project into SpringToolSuite or your favorite IDE.
Edit the application properties.
src/main/resources/application.properties
server.port=8080
spring.application.name=sample-webapp
service.base.url=http://localhost:8081
Edit the application main to include a RestTemplate bean.
src/main/java/com/example/service/WebappApplication.java
package com.example.webapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class WebappApplication {
public static void main(String[] args) {
SpringApplication.run(WebappApplication.class, args);
}
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
Add a simple controller and use annotations to create a span.
src/main/java/com/example/service/RootController.java
package com.example.webapp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.sleuth.annotation.NewSpan;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Controller
public class RootController {
@Autowired
RestTemplate restTemplate;
@Value( "${service.base.url}" )
private String serviceBaseUrl;
@GetMapping("/")
@NewSpan("RootController#helloWorld")
@ResponseBody
public String helloWorld() {
log.info("Handling home");
return restTemplate.getForEntity(serviceBaseUrl+"/", String.class).getBody();
}
}
We can start our sample service and web app now in your IDE or at the command line. Note the -pl flag is required because I am using a multimodule pom
mvn spring-boot:run -pl service
mvn spring-boot:run -pl webapp
Open a browser to http://localhost:8080/
will generate a trace. Now go to http://localhost:9411/ and run a query for the generated trace.
Build the images. You should replace jameskolean
whith your docker hub ID.
mvn -pl webapp spring-boot:build-image -Dspring-boot.build-image.imageName=jameskolean/tracing-spring-boot-webapp-sample
mvn -pl service spring-boot:build-image -Dspring-boot.build-image.imageName=jameskolean/tracing-spring-boot-service-sample
Create a docker-compose to start all the services.
docker-compose.yml
version: '3'
services:
zipkin:
image: openzipkin/zipkin
container_name: zipkin
ports:
- '9411:9411'
service-sample:
image: jameskolean/tracing-spring-boot-service-sample
container_name: service-sample-1
ports:
- '8081:8081'
environment:
- spring.zipkin.base-url=http://zipkin:9411
- spring.application.instance_id=sample-1
webapp-sample:
image: jameskolean/tracing-spring-boot-webapp-sample
container_name: webapp-sample-1
ports:
- '8080:8080'
environment:
- service.base.url=http://service-sample:8081
- spring.zipkin.base-url=http://zipkin:9411
- spring.application.instance_id=webapp-1
Make sure that all docker images are stopped. Start the services.
docker-compose up
Opening a browser to http://localhost:8080/
will generate a trace.
Now go to http://localhost:9411/ and run a query for the generated trace.