目录
正在加载目录…
专栏文章
专栏文章
日志专栏
1. 日志体系:类型、Schema 与安全基线 2. 业务与审计日志:领域事件、AOP 与 Outbox 3. 分布式追踪:OTel、MDC 与异步传播 4. 日志平台:采集、存储与 SLO 告警

分布式追踪:OTel、MDC 与异步传播

发布于 2026-08-10 14:48 · 最后编辑于 2026-08-10 14:48 · 字数 1,987 👁 52 次阅读

分布式日志关联的默认解法是 OpenTelemetry 上下文与 W3C Trace Context;MDC 只是把当前上下文投影到日志的桥梁。本文说明两者的分工、异步边界和 Spring Boot 的接入策略。

目录

章节说明
关联模型trace、span、request 的职责
默认方案:OTel 与 W3C传播标准和日志关联
MDC 的正确位置生命周期、清理与补充上下文
异步与消息边界Executor、Reactor、Kafka 的传播原则
Spring Boot 接入使用 Micrometer Tracing 的建议
手工方案的适用范围遗留系统的最小安全实现

关联模型

标识含义使用边界
trace_id一次端到端因果链路32 位十六进制;跨进程传播
span_id链路中的一个操作16 位十六进制;用于定位具体 RPC/DB 操作
request_id网关或产品定义的一次请求标识可独立存在,不要冒充 trace ID
baggage跨服务的附加上下文只放小型、非敏感、低基数的数据,设置白名单

一次请求会共享 trace_id,每个服务/操作产生自己的 span_id。日志中同时输出两者,才能既按整条链路查询,也能定位某个调用。标准格式见 OpenTelemetry Trace API

默认方案:OTel 与 W3C

新系统应由 OpenTelemetry/Micrometer 自动创建上下文,并用 W3C traceparent 头在 HTTP、RPC 和消息属性中传播。不要自行定义 X-Trace-Id 作为默认协议;若必须兼容旧系统,应在边界做一次明确的转换和输入校验。

sequenceDiagram
    participant C as Client
    participant G as Gateway
    participant O as Order Service
    participant P as Payment Service
    C->>G: traceparent
    G->>O: traceparent(继承 trace,创建子 span)
    O->>P: traceparent(继承 trace,创建子 span)
    Note over G,P: 每条日志输出 trace_id 与 span_id

传播时不得盲目信任互联网来源的追踪头:入口应限制格式和长度,并将租户、用户身份等安全语义留在认证上下文,而不是由 trace header 决定。

MDC 的正确位置

MDC 是日志框架的线程本地键值上下文,适合在日志 pattern/JSON encoder 中输出 trace_idspan_idrequest_id。它不是分布式追踪协议,也不能自行跨线程。

手工使用时必须成对设置和恢复,而不是简单 clear() 覆盖调用方已有上下文:

Map<String, String> previous = MDC.getCopyOfContextMap();
Map<String, String> next = Map.of("request_id", requestId);
try {
    MDC.setContextMap(next);
    chain.doFilter(request, response);
} finally {
    if (previous == null) {
        MDC.clear();
    } else {
        MDC.setContextMap(previous);
    }
}

在 Servlet 线程结束、线程池任务结束和消息消费完成时都应清理/恢复。对 OTel 已自动关联的应用,优先使用框架提供的日志关联能力,不要再手动生成第二套 trace ID。

异步与消息边界

边界首选方案注意点
Executor / CompletableFuture使用 OTel context-aware executor 或框架自动 instrumentation任务提交时捕获上下文,任务完成后恢复原上下文
Reactor / WebFlux使用 OTel Reactor instrumentationMDC 依赖线程本地,不能假设跨 operator 自动可用
Kafka/RabbitMQ使用 OTel instrumentation 注入/提取消息 headers消费者创建处理 span;处理重试和 DLQ 要保留关联
定时/批处理任务显式创建 root span 或链接到触发事件不要伪造来自 HTTP 请求的父上下文

不要只重写 Executor.executesubmitinvokeAll 等提交路径也会绕过它。TransmittableThreadLocal 也不是“加依赖即可传播 MDC”;它需要相应的上下文 adapter 或任务包装,且不能替代 OTel 在跨服务边界的标准传播。

Spring Boot 接入

Spring Boot 3.x 推荐以 Micrometer Tracing 作为应用层接入,底层可选择 OpenTelemetry 或 Brave bridge;导出至兼容后端后,框架会负责常见 HTTP 客户端、服务端和消息场景的上下文传播。实际能力取决于已启用的 instrumentation,应在集成测试中验证每条边界。

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>

日志 encoder 应输出统一 Schema 中的 trace_idspan_id,并避免自动将全部 MDC 键无筛选地写出。Spring Boot 的具体配置与采样方式以官方 Tracing 文档为准。

SkyWalking Java Agent 仍适合作为低侵入的补充方案,但“无需修改一行代码”不等于零配置或覆盖所有库;应确认 agent 插件、异步调用、采样和日志关联字段,再决定是否采用。

手工方案的适用范围

手工 MDC/自定义 header 仅适用于无法接入 OTel 的遗留服务、短期兼容或非常受限的单体系统。此时至少做到:

  1. 入口生成或校验 ID,长度和字符集受限;不能让外部输入污染日志。
  2. HTTP、MQ、线程池均有成对的注入/提取与恢复逻辑。
  3. 统一采用 trace_id 字段,不与 traceIdtid 等并存。
  4. 明确监控传播失败、丢失率和异常链路,不把 grep 当作唯一排障手段。

参考资料

← 返回列表

评论 (0)

暂无评论,来留下第一条吧。
登录注册 后才能发表评论