Spring Cloud Sleuth Mapped Diagnostic Context (MDC)

好的,各位观众,各位码农,各位敲键盘的英雄们!今天咱们来聊聊 Spring Cloud Sleuth 的 MDC,也就是俗称的“麻袋”,啊不,是“马甲”! 别紧张,我不是要讲马甲线,更不是要讲怎么伪装身份,而是讲怎么在微服务架构里穿上“马甲”,让日志也能追根溯源,找到是谁干的!

准备好了吗?系好安全带,咱们要开车了!

开场白:微服务丛林探险,迷路的日志君

各位都知道,现在流行微服务。 以前单体应用,一个日志文件,啥事儿都清清楚楚,就像一个大家庭,谁放了个屁,大家都知道是谁。 现在好了,微服务一搞,变成了一个热带雨林,几十个、几百个服务,各自为政,各自写日志,就像几十个家庭,谁放了个屁,谁都不知道是谁,只能闻到一股迷之味道。

在这种情况下,出了问题,想追踪一下请求链路,那简直是灾难! 就像大海捞针,或者在热带雨林里找一根头发丝,难度系数五颗星!

想想看,一个用户请求,经过了 A 服务、B 服务、C 服务…最终报错了。 你打开 A 服务的日志,OK,看到了,请求进来了。 再打开 B 服务的日志,嗯,也看到了,A 服务调了它。 但是…C 服务的日志呢? 怎么没有这个请求的踪影? 是不是 C 服务挂了?还是 A 服务根本没调 C 服务? 或者…日志配置有问题?

抓狂啊! 简直是程序员的噩梦! 😭

所以,为了解决这个问题,Spring Cloud Sleuth 带着它的好基友 MDC 闪亮登场了!

第一幕:Sleuth 和 MDC 的爱情故事

Sleuth 是个啥? 简单来说,它就是一个链路追踪的工具。 它可以给每个请求分配一个唯一的 ID,就像给每个请求贴上一个身份证。 然后,它会把这个 ID 传递给所有参与处理这个请求的服务。

MDC 又是啥? MDC,全称 Mapped Diagnostic Context,可以理解成一个“线程级别的全局变量”。 你可以在 MDC 里放一些信息,比如请求 ID、用户 ID 等等。 然后,你在日志配置里,就可以把这些信息打印出来。

Sleuth 和 MDC 联手,简直就是天作之合!

  • Sleuth 负责生成和传递请求 ID (Trace ID) 和 Span ID (跨度 ID)。 Trace ID 代表一个完整的请求链路,Span ID 代表链路中的一个单独的调用。
  • Sleuth 负责把 Trace ID 和 Span ID 放到 MDC 里。
  • 你的日志配置负责从 MDC 里把 Trace ID 和 Span ID 打印出来。

这样,每个日志都会带上 Trace ID 和 Span ID,你就可以根据 Trace ID 把一个请求的所有日志串起来,像串糖葫芦一样,清晰明了!

第二幕:代码实战,手把手教你穿“马甲”

理论讲完了,咱们来点实际的,看看怎么在代码里使用 Sleuth 和 MDC。

1. 引入依赖:

首先,在你的 pom.xml 文件里,加上 Sleuth 的依赖:

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

Spring Boot 会自动帮你配置好 Sleuth。 是不是很简单? 就像一键安装软件一样!

2. 配置日志:

接下来,你需要配置你的日志框架(比如 Logback、Log4j2)来打印 MDC 里的信息。

以 Logback 为例,在你的 logback-spring.xml 文件里,加上以下配置:

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - [%X{traceId},%X{spanId}] - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

注意看 %X{traceId}%X{spanId} 这两个占位符。 它们就是用来从 MDC 里获取 Trace ID 和 Span ID 的。

如果你用的是 Log4j2,配置也类似,只是占位符稍微有点不同: %mdc{traceId}%mdc{spanId}

3. 启动你的微服务:

好了,配置都搞定了,启动你的微服务吧!

4. 发起一个请求:

现在,发起一个请求,让你的微服务跑起来。

5. 查看日志:

打开你的日志文件,你会看到这样的日志:

2023-10-27 10:00:00.000 [http-nio-8080-exec-1] INFO  com.example.demo.controller.DemoController - [a1b2c3d4e5f6g7h8,i9j0k1l2m3n4o5p6] - Received request
2023-10-27 10:00:01.000 [http-nio-8080-exec-2] INFO  com.example.demo.service.DemoService - [a1b2c3d4e5f6g7h8,q7r8s9t0u1v2w3x4] - Processing request

看到没? 每条日志都带上了 [traceId,spanId],就像穿上了统一的制服,一眼就能认出来它们属于同一个请求链路!

第三幕:进阶玩法,玩转 Sleuth 和 MDC

掌握了基本用法,咱们再来点高级的玩法。

1. 自定义 Trace ID 生成器:

Sleuth 默认会生成随机的 Trace ID 和 Span ID。 如果你想自定义 Trace ID 的生成规则,可以实现 TraceIdGenerator 接口。

比如,你可以把请求的时间戳加到 Trace ID 里,这样就可以更容易地根据时间来查找日志。

2. 自定义 Span:

有时候,你需要手动创建一个 Span,来追踪一些特殊的操作。

比如,你想追踪一个数据库查询的耗时,可以这样做:

@Autowired
Tracer tracer;

public void doSomething() {
    Span span = tracer.nextSpan().name("Database Query").start();
    try (Tracer.SpanInScope ws = tracer.withSpan(span.start())) {
        // 执行数据库查询
        databaseQuery();
    } finally {
        span.end();
    }
}

这段代码会创建一个名为 "Database Query" 的 Span,并在数据库查询前后记录 Span 的开始和结束时间。

3. 集成其他组件:

Sleuth 可以和很多其他的组件集成,比如 Spring Cloud Gateway、Feign、RestTemplate 等等。 这些组件会自动帮你传递 Trace ID 和 Span ID,你不需要手动处理。

第四幕:常见问题,帮你避坑

在使用 Sleuth 和 MDC 的过程中,可能会遇到一些问题。 这里我总结了一些常见的问题,帮你避坑。

  • 问题:日志里没有 Trace ID 和 Span ID。

    • 原因:
      • Sleuth 没有正确配置。 检查你的 pom.xml 文件里是否引入了 Sleuth 的依赖。
      • 日志配置有问题。 检查你的 logback-spring.xmllog4j2.xml 文件里是否正确配置了 MDC 的占位符。
      • 请求没有经过 Sleuth 的拦截器。 检查你的 Web 应用是否正确配置了 Sleuth 的拦截器。
  • 问题:Trace ID 和 Span ID 都是 null。

    • 原因:
      • Sleuth 的配置不生效。 检查你的 Spring Boot 配置文件里是否有相关的配置覆盖了 Sleuth 的默认配置。
      • 线程上下文丢失。 在使用线程池或者异步任务的时候,可能会导致线程上下文丢失,从而导致 Trace ID 和 Span ID 丢失。 可以使用 ThreadLocal 或者 InheritableThreadLocal 来传递线程上下文。
  • 问题:Span ID 不连续。

    • 原因:
      • 异步调用。 如果你的服务使用了异步调用,可能会导致 Span ID 不连续。 这是正常的,因为异步调用会创建新的线程,从而创建新的 Span。
      • 采样率。 Sleuth 默认会对请求进行采样,只有被采样的请求才会生成 Trace ID 和 Span ID。 你可以通过配置 spring.sleuth.sampler.probability 来调整采样率。

第五幕:总结与展望,拥抱云原生时代

好了,各位,今天的 Sleuth 和 MDC 之旅就到这里了。 咱们一起学习了 Sleuth 和 MDC 的基本概念、使用方法和常见问题。

总结:

  • Sleuth 负责生成和传递 Trace ID 和 Span ID。
  • MDC 负责存储 Trace ID 和 Span ID。
  • 日志配置负责从 MDC 里把 Trace ID 和 Span ID 打印出来。

展望:

随着云原生时代的到来,微服务架构越来越流行,链路追踪的重要性也越来越凸显。 Sleuth 作为 Spring Cloud 家族的一员,将会继续发挥重要的作用。

未来,Sleuth 可能会朝着以下几个方向发展:

  • 更强大的链路追踪能力: 支持更多的协议和框架,提供更详细的链路追踪信息。
  • 更智能的分析能力: 自动分析链路追踪数据,发现性能瓶颈和异常。
  • 更好的可观测性: 与 Prometheus、Grafana 等可观测性工具集成,提供更全面的监控和告警。

希望各位码农能够熟练掌握 Sleuth 和 MDC,在微服务丛林里自由穿梭,不再迷路!💪

附录:实用表格

概念 解释 作用
Trace ID 一个请求链路的唯一标识符。 就像一个身份证号码,代表一个完整的请求流程。 把一个请求的所有日志串起来,方便追踪请求链路。
Span ID 一个请求链路中的一个单独的调用。 就像一个人的不同行为,代表请求流程中的一个步骤。 标识一个请求链路中的具体操作,方便分析性能瓶颈。
MDC Mapped Diagnostic Context,可以理解成一个“线程级别的全局变量”。 存储 Trace ID、Span ID 等信息,方便在日志里打印。
Sleuth Spring Cloud 链路追踪工具。 负责生成和传递 Trace ID 和 Span ID,并把它们放到 MDC 里。

希望这篇文章能够帮助你更好地理解 Spring Cloud Sleuth 的 MDC。 如果你有任何问题,欢迎留言交流! 👍

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注