首页 / Spring Boot 入门教程 / 日志与追踪

Spring Boot 入门教程

日志与追踪

本教程共 48 篇 · 第 38 篇 · 更新于 2026-08-13 · 约 5 分钟阅读

Spring Boot分布式追踪Micrometer TracingZipkinOpenTelemetry日志关联可观测性

本节目标:理解可观测性三支柱,用 Micrometer Tracing 接入 Zipkin,让 traceId 自动进日志,掌握跨服务传递与自定义埋点。

可观测性三支柱

应用出问题,靠三样东西定位:

  • 日志(Logs):发生了什么
  • 指标(Metrics):系统状态如何
  • 追踪(Traces):一次请求走了哪些环节

对应的工具链:ELK 管日志,Prometheus 加 Grafana 管指标,Zipkin 或 Jaeger 管追踪。

单体应用里看日志就够。微服务一多,一个请求串起五六个服务,每处日志各写各的,根本对不上。

追踪就是干这个的:给请求发一张身份证(traceId),全链路共享。

核心概念:Trace 与 Span

  • Trace:一次完整请求,由多个 Span 组成
  • Span:请求中的一个环节(一次 HTTP 调用、一次数据库查询)
  • traceId:整条链路的统一编号
  • spanId:每个环节自己的编号,记录父子关系

Zipkin、Jaeger 这类系统把 Span 收集起来,画成瀑布图,一眼看出哪里慢。比如「下单接口 2 秒」的真相,可能是「库存服务慢」而不是你的代码慢。

Micrometer Tracing 起步

Spring Cloud Sleuth 已停止维护。Spring Boot 3 起统一用 Micrometer Tracing,4.1.0 内置支持两种后端:

  • Brave + Zipkin:spring-boot-starter-zipkin
  • OpenTelemetry + OTLP:spring-boot-starter-opentelemetry

先用 Zipkin 路线:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-zipkin</artifactId>
</dependency>

默认只采样 10% 的请求。开发环境调成 100%:

management:
  tracing:
    sampling:
      probability: 1.0

采样率是成本和覆盖面的权衡:全量采样会压垮追踪后端,生产环境 10% 是常见起步值。

启动本地 Zipkin:

docker run -d -p 9411:9411 openzipkin/zipkin

跑起应用,访问几个接口,打开 http://localhost:9411,能看到每个请求的完整调用链。HTTP 请求的 Span 是自动创建的,一行代码都不用写。

Note

版本对照:老教程的 spring-cloud-starter-sleuth 在 Boot 3 起不可用,统一改用 Micrometer Tracing 的 starter。Sleuth 项目本身也已停止维护。

日志关联:traceId 进日志

追踪系统里有链路,日志里也得能对上号。Micrometer Tracing 默认把 traceId、spanId 写进 MDC,日志自动带上关联 ID:

2026-08-13T20:00:00.123+08:00  INFO [803B448A0489F84084905D3093480352-3425F23BB2432450] ...

格式是 [traceId-spanId]。想改成 Sleuth 老格式:

logging:
  pattern:
    correlation: "[${spring.application.name:},%X{traceId:-},%X{spanId:-}] "
  include-application-name: false

MDC 是日志框架的「请求上下文」,traceId 存进去,这条请求打的每条日志都带上。

有了这个 ID,拿日志里的 traceId 去 Zipkin 搜,链路和日志就串起来了。排障流程变成:日志里找 traceId → Zipkin 里看全链路 → 定位慢的环节。

跨服务传递

服务 A 调服务 B,traceId 要靠 HTTP 头传过去(W3C traceparent 或 B3 头)。用自动配置的客户端构建器,传递自动完成:

@Service
public class OrderClient {

    private final RestClient restClient;

    public OrderClient(RestClient.Builder builder) {
        this.restClient = builder.baseUrl("http://order-service").build();
    }
}
Warning

自己 new RestClient() / new WebClient() 不会带追踪头。必须注入自动配置的 RestClient.BuilderWebClient.BuilderRestTemplateBuilder。这是排查「追踪断链」的第一嫌疑。

自定义 Span 与 Baggage

HTTP 请求自动有 Span。业务代码想埋点,用 Observation API:

package com.example.demo.service;

import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.stereotype.Service;

@Service
public class PaymentService {

    private final ObservationRegistry registry;

    public PaymentService(ObservationRegistry registry) {
        this.registry = registry;
    }

    public void pay(String orderId) {
        Observation.createNotStarted("payment.process", registry)
                .lowCardinalityKeyValue("channel", "wechat")
                .highCardinalityKeyValue("orderId", orderId)
                .observe(() -> {
                    // 支付逻辑
                });
    }
}
  • 低基数标签(渠道、方法)会进指标和追踪
  • 高基数标签(订单号)只进追踪,防止指标爆炸

Observation 是 Micrometer 的统一埋点入口:一个观察同时产出指标和追踪,不用两套 API。

想把自定义字段带进日志(MDC),用 Baggage:

import io.micrometer.tracing.BaggageInScope;
import io.micrometer.tracing.Tracer;

try (BaggageInScope scope = tracer.createBaggageInScope("userId", "42")) {
    // 这段代码里打的日志自动带 userId
}

配合配置:

management:
  tracing:
    baggage:
      correlation:
        fields: "userId"

Baggage 和标签的区别:Baggage 会跟着请求跨服务传递,适合放用户 ID 这类「全链路都要」的字段。

异步线程的上下文传播

追踪上下文默认存在 ThreadLocal 里。@Async 方法换了线程,上下文就丢了。

自动配置的 AsyncTaskExecutor 上打开传播开关:

spring:
  task:
    execution:
      propagate-context: true

自己创建线程池的话,注册一个 ContextPropagatingTaskDecorator Bean。漏了这一步,异步任务的日志会没有 traceId,链路在异步边界断掉。

日志文件与 logfile 端点

追踪之外,日志本身也有运维入口。配置日志文件后,/actuator/logfile 端点能在线看日志:

logging:
  file:
    name: "logs/app.log"

配合 loggers 端点:先调级别,再拉日志,问题定位一气呵成。日志文件配合 logrotate 之类工具切分,别让单文件无限长大。

运行时调日志级别

loggers 端点支持在线调整,不用重启:

# 查看某个 logger
curl http://localhost:8080/actuator/loggers/com.example.demo

# 调成 DEBUG
curl -X POST http://localhost:8080/actuator/loggers/com.example.demo \
  -H "Content-Type: application/json" \
  -d '{"configuredLevel": "DEBUG"}'

排查线上问题时,先把目标包调成 DEBUG,查完再调回来。

OpenTelemetry 路线

不用 Zipkin,直接走 OpenTelemetry:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-opentelemetry</artifactId>
</dependency>
management:
  opentelemetry:
    tracing:
      export:
        otlp:
          endpoint: "http://collector:4318/v1/traces"

OpenTelemetry 生态大:OTLP 协议、OTEL_* 环境变量、与 Grafana Tempo、Jaeger 等后端互通。日志也可以走 OTLP 导出,在 logback-spring.xml 里挂 OpenTelemetryAppender。

选型建议:团队已有 Zipkin 用 Brave 路线最省事;从零建设、考虑多语言统一,OpenTelemetry 是趋势。

初学者常栽的坑

  • 采样率调成 1.0 忘了调回来:生产流量十倍打进 Zipkin,后端直接被打趴
  • 自己 new 的 RestClient 追踪断链:必须用自动配置的 Builder(见上文警告)
  • @Async 方法日志没有 traceId:没开上下文传播,链路在异步边界断掉
  • 日志和追踪对不上:确认 logging.pattern.correlation 里保留了 traceId
  • 只接 Zipkin 不接日志系统:链路再全,日志里找不到上下文也白搭

本节小结

  • 三支柱:日志、指标、追踪,各回答一个问题
  • Micrometer Tracing 接 Brave+Zipkin 或 OpenTelemetry+OTLP
  • 默认采样 10%,开发调 management.tracing.sampling.probability=1.0
  • traceId 自动进日志 MDC,用 logging.pattern.correlation 改格式
  • 客户端必须用自动配置的 Builder 才能传递追踪头
  • 异步线程要开上下文传播,spring.task.execution.propagate-context=true
  • loggers 端点在线调日志级别,不用重启