Generic Top

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

In this article, we'll take a look at Spring Cloud Sleuth and see how we can use it for tracing in Spring Boot. It adds useful, extra information to our logs and makes it easier to debug actions by adding unique identifiers to them. These actions are called traces in Sleuth terminology. They can consist of several steps, called spans.

For example, a trace can be a GET request that is querying data from our application. When our application processes the request, it can be split up into smaller steps: user authorization, executing the database query, transforming the response. Each of these steps is a unique span belonging to the same trace.

In some cases, we might want to get the ID of the current trace or span. For instance, we could send these to the development team when there is an incident. Then they can use this to debug and fix the problem.

2. Application Setup

Let’s start by creating a Spring Boot project and adding the spring-cloud-starter-sleuth dependency:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
    <version>3.1.0</version>
</dependency>

This starter dependency integrates well with Spring Boot and provides the necessary configuration to start using Spring Cloud Sleuth.

However, there is one extra step we can take. Let’s set the name of our application in the application.properties file, this way we'll see this in the logs along with the trace and span IDs:

spring.application.name=Baeldung Sleuth Tutorial

Now we need an entry point to our application. Let’s create a REST controller with a single GET endpoint:

@RestController
public class SleuthTraceIdController {

    @GetMapping("/traceid")
    public String getSleuthTraceId() {
        return "Hello from Sleuth";
    }
}

Let's visit our API endpoint at http://localhost:8080/traceid. We should see “Hello from Sleuth” in the response.

3. Logging

Let's add a log statement to the getSleuthTraceId method. First, we need a Logger for our class. Then we can log the message:

private static final Logger logger = LoggerFactory.getLogger(SleuthTraceIdController.class);

@GetMapping("/traceid")
public String getSleuthTraceId() {
    logger.info("Hello with Sleuth");
    return "Hello from Sleuth";
}

Let’s call our API endpoint again and check the logs. We should find something similar to this:

INFO [Baeldung Sleuth Tutorial,e48f140a63bb9fbb,e48f140a63bb9fbb] 9208 --- [nio-8080-exec-1] c.b.s.traceid.SleuthTraceIdController : Hello with Sleuth

Notice that the application name is inside the brackets at the beginning. These brackets are added by Sleuth. They represent the application name, trace ID, and span ID.

4. Current Trace and Span

We can use the above example to debug issues in our application, but it might be challenging to determine what caused it and which trace to follow. That’s why we'll get the current trace programmatically, and then we can use it for any further investigations.

In our implementation, we'll simplify this use case, and we'll just log the trace IDs to the console.

Firstly, we need to get an instance of a Tracer object. Let’s inject it into our controller and get the current span:

@Autowired
private Tracer tracer;

@GetMapping("/traceid")
public String getSleuthTraceId() {
    logger.info("Hello with Sleuth");
    Span span = tracer.currentSpan();
    return "Hello from Sleuth";
}

Notice that the currentSpan method can return null if there is no active span at the moment. Therefore we have to perform an additional check to see if we can proceed and use this Span object without getting NullPointerException. Let’s implement this check and log the current trace and span IDs:

Span span = tracer.currentSpan();
if (span != null) {
    logger.info("Trace ID {}", span.context().traceIdString());
    logger.info("Span ID {}", span.context().spanIdString());
}

Let’s run the application and look for these messages when we visit our API endpoint. They should contain the same IDs as the brackets added by Sleuth.

5. Trace and Span ID as Decimal Numbers

There is another way to get the span ID with the spanId method instead of spanIdString. The difference between them is that the latter one returns the hexadecimal representation of the value while the first one returns a decimal number. Let’s compare them in action and log the decimal value as well:

Span span = tracer.currentSpan();
if (span != null) {
    logger.info("Span ID hex {}", span.context().spanIdString());
    logger.info("Span ID decimal {}", span.context().spanId());
}

The two values represent the same number, and the output should look similar to this:

INFO [Baeldung Sleuth Tutorial,0de46b6fcbc8da83,0de46b6fcbc8da83] 8648 --- [nio-8080-exec-3] c.b.s.traceid.SleuthTraceIdController    : Span ID hex 0de46b6fcbc8da83
INFO [Baeldung Sleuth Tutorial,0de46b6fcbc8da83,0de46b6fcbc8da83] 8648 --- [nio-8080-exec-3] c.b.s.traceid.SleuthTraceIdController    : Span ID decimal 1001043145087572611

Similarly, this applies to trace IDs as well. Instead of traceIdString, we can use the traceId method. traceIdString returns a hexadecimal value while traceId returns a decimal value:

logger.info("Trace ID hex {}", span.context().traceIdString());
logger.info("Trace ID decimal {}", span.context().traceId());

The output is very similar to the previous one. It contains the trace ID in hexadecimal first then in decimal:

INFO [Baeldung Sleuth Tutorial,34ec0b8ac9d65e91,34ec0b8ac9d65e91] 7384 --- [nio-8080-exec-1] c.b.s.traceid.SleuthTraceIdController    : Trace ID hex 34ec0b8ac9d65e91
INFO [Baeldung Sleuth Tutorial,34ec0b8ac9d65e91,34ec0b8ac9d65e91] 7384 --- [nio-8080-exec-1] c.b.s.traceid.SleuthTraceIdController    : Trace ID decimal 3813435675195629201

6. Conclusion

In this article, we discussed how Spring Cloud Sleuth could help debug and trace events in Spring Boot. First, we used the Tracer object to reference the current span and the TraceContext. After that, we were able to get the ID of the current trace and span. In addition, we saw how different methods return the ID in different number systems.

As always, the source code with these examples is available over on GitHub.

Generic bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
Cloud footer banner
Comments are closed on this article!