Spring Cloud Sleuth 获取当前 Trace ID

1、概览

本文将带你了解 Spring Cloud Sleuth,以及如何在 Spring Boot 中使用它进行链路追踪。

它可以在日志中添加额外有用的信息,并通过唯一链路 ID 帮助 Debug。这些操作在 Sleuth 术语中称为追踪(Trace)。它们可以由多个步骤组成,称为 Span

例如,链路追踪可以是一个从应用中查询数据的 GET 请求。当应用处理该请求时,可以将其分割成更小的步骤:用户授权、执行数据库查询、转换响应。每个步骤都是属于同一链路追踪的唯一 Span。

在某些情况下,我们可能需要获取当前 Trace 或 Span 的 ID。例如,当发生事故时,我们可以将这些信息发送给开发团队。然后,他们就可以用它来调试和解决问题。

2、应用设置

创建一个 Spring Boot 项目,并添加 spring-cloud-starter-sleuth dependency 依赖:

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

此 Starter 可与 Spring Boot 完美集成,并提供 Spring Cloud Sleuth 开箱即用的配置。

接下来,在 application.properties 文件中设置应用名称,这样就能在日志中看到该名称以及 Trace 和 Span ID:

spring.application.name=Baeldung Sleuth Tutorial

现在,创建一个 REST Controller,提供一个 GET 端点:

@RestController
public class SleuthTraceIdController {

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

启动应用后访问这个 API 端点 http://localhost:8080/traceid,你可以看到响应:Hello from Sleuth

3、日志

getSleuthTraceId 方法中添加日志输出。

创建一个 Logger,然后就可以输出日志了。

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

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

再次调用 API 端点并查看日志。你应该能看到类似于如下的内容:

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

注意,应用名称位于开头的括号内。这些括号是 Sleuth 添加的。它们代表应用名称、Trace ID 和 Span ID

4、当前 Trace 和 Span

我们可以使用上述示例来 Debug 应用中的问题,但要确定问题的起因和追踪哪个链路可能会很困难。这就是为什么要以编程方式获取当前 Trace,然后利用它进行进一步调查。

在本例中,简化了实现,只在控制台中记录 Trace ID。

首先,在 Controller 中注入 Tracer 对象,用于获取当前 Span:

@Autowired
private Tracer tracer;

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

注意,如果当前没有活动 Span,则 currentSpan 方法可能返回 null。因此,必须执行额外的检查,以确定是否可以继续使用该 Span 对象,避免 NullPointerException

记录当前 Trace 和 Span ID:

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

运行应用,并在访问 API 端点时查看这些信息。它们应该与 Sleuth 添加的括号中的 ID 相同。

5、十进制的 Trace ID 和 Span ID

还有另一种获取 Span ID 的方法,即使用 spanId 方法而不是 spanIdString 方法。它们的区别在于,后一种方法返回值的十六进制表示法,而前一种方法返回的是十进制数。

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

这两个值代表相同的数值,输出结果类似于如下:

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

同样,这也适用于 Trace ID。traceIdString 返回十六进制值,而 traceId 返回十进制值:

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

输出结果和上面类似。首先是十六进制的 Trace ID,然后是十进制:

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、总结

本文介绍了如何通过 Spring Cloud Sleuth 追踪 Spring Boot 中的事件,以帮助 Debug。


参考:https://www.baeldung.com/spring-cloud-sleuth-get-trace-id