日志与追踪
本教程共 48 篇 · 第 38 篇 · 更新于 2026-08-13 · 约 5 分钟阅读
本节目标:理解可观测性三支柱,用 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.Builder、WebClient.Builder或RestTemplateBuilder。这是排查「追踪断链」的第一嫌疑。
自定义 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 端点在线调日志级别,不用重启