日志里出现 trace_id,就等于送进 Logs SDK 吗?

业务日志的文本带了 Trace ID,只能证明某条输出可供人关联。要将其变为 OTel LogRecordData,Logback 还需另一个 appender 将事件映射到 Logs API。本篇用同一 logger 的三个阶段核对:普通 ListAppender → MDC 装饰器包住 ListAppender → 额外安装 Logs 桥接 appender。每个阶段只发一条日志,禁用向 root logger 传播,避免实验误把重复传播当成桥接行为。

阶段 ListAppender 新增事件 trace_id 在输出事件 MDC 中 内存 Logs exporter 新增记录
普通日志、无 Span 1 无 0
仅 MDC 装饰器、有效 Span 1 有 0
MDC 与 OTel appender、有效 Span 1 有 1

两条源码路径不要混成一条

instrumentation 仓库固定 tag v1.31.0,完整 SHA 97d87f3f3b2bc61aa396d71a98387a1e857618af;两个 artifact 均为该发行版的 1.31.0-alpha library,不是 Agent。以下为静态源码和上游测试核查。

MDC library 的 OpenTelemetryAppender.wrapEvent 从当前 Context 查有效 SpanContext,把 trace_id、span_id、trace_flags 加到包装事件的 MDC;若事件已有 trace_id,则跳过本次装饰。其 append 只向内部挂载的 appender 转发事件,不调用 Logs SDK。上游 AbstractLogbackTest 对比无 Span 与有 Span 时 MDC ID 的出现。

Logs library 的 OpenTelemetryAppender.start 创建 mapper;未设置 SDK 时默认使用 noop OpenTelemetry。append 把事件送给 getLogsBridge()。LoggingEventMapper 设置格式化 body、毫秒事件时间、severity 和 当前 Context;MDC 默认不采集,需通过 setCaptureMdcAttributes 指定键。上游 OpenTelemetryAppenderTest.logWithSpan 验证有效与无效 SpanContext;LoggingEventMapperTest.testSome 核对键的白名单映射。

这里需要注意线程边界:MDC appender 装饰的是下游 Logback 事件;另一个 OTel appender 使用自己的 Context.current(),本例两者都在同一个 Scope 和线程里调用。若先脱离 Scope、再异步重放事件,不能从 MDC 中存在 ID 推出新的 OTel LogRecord 也携带相同的 SpanContext。

跑通受控对照

Lab19Test 使用 Logback 1.4.14、SLF4J 2.0.7;两个 OTel instrumentation library 的坐标、SHA-256、SDK 版本详见 writing-plans/opentelemetry-java/VERSIONS.md。使用 ListAppender 检查普通日志/MDC,用 SimpleLogRecordProcessor 和进程内 exporter 检查 Logs 桥接。不运行 Java Agent。测试在工程目录执行:

1
JAVA_HOME=/tmp/otel-20260930/jdk-extract/usr/lib/jvm/java-21-openjdk-amd64 ./mvnw -q -pl sdk-labs -Dtest=Lab19Test test

退出码 0,原始摘要:

1
LAB19 plain=1,mdcOnly=1,otelRecords=1,mdcTraceMatch=true,logTraceMatch=true,route=/orders

MDC 路径的 trace_id 与 span_id 等于当前 Span;Logs 路径记录的 SpanContext 也与 Span 相等,白名单 route 映射为 logback.mdc.route=/orders。数量断言证明当前配置的第三条日志只进入一次 OTel exporter,不覆盖双重安装相同 OTel appender、向父 logger 传播等情况。实验没有 Log OTLP 网络导出,不能宣称 Collector 已收到日志。

易错判断与练习

“MDC 注入会自动把日志传给 Collector”忽略了 Logs 桥接、processor、exporter 和网络;“只要 MDC 有 Trace ID,Logs SDK 便会复用它”也不符合上述 mapper 的当前 Context 路径。setCaptureMdcAttributes("*") 还会把所有 MDC 键送进属性;认证令牌若放入 MDC,将带来泄漏风险,本实验只选 route。

  1. 在 Lab19Test 中删除 OTel appender,只保留 MDC 装饰器,复跑并解释为何日志事件有 trace_id,内存 exporter 仍为 0。
  2. 把 setCaptureMdcAttributes("route") 改成指定不存在的键,断言 logback.mdc.route 消失,而记录的 SpanContext 仍匹配;这个反例说明属性捕获与关联上下文是两条路径。

参考资料:instrumentation v1.31.0 Logback 源码与上游测试;运行、审校与冻结清单见 examples/opentelemetry-java/evidence/19/RUN.md。

导航:18 Logs SDK · 当前篇:19 Logback 桥接与 MDC。