本文定位:面向需要治理异步链路追踪的团队,尤其是已经接入基础 Trace,但在消息队列、异步任务、回调或重试链路中经常丢失上下文的平台、研发和 SRE 团队。
消息队列接入后,原本连续的 Trace 经常被切成生产端和消费端两段。异步链路追踪要解决的,就是让一次业务请求从同步入口进入队列、再被消费者处理时,仍然能被还原为同一条可解释的调用链,而不是生成一串互不相干的 trace_id。
如果团队还在建设完整接入流程,可以先参考站内 OpenTelemetry链路追踪怎么做;如果问题集中在 Collector 管道、采样和导出,可结合 OpenTelemetry Collector管道部署 排查。本文只聚焦异步边界中的断链问题。

图1:异步调用链路边界与断点位置
先判断断链发生在哪个异步边界
消息链路通常会跨过三个不同责任域:生产端应用、消息系统和消费端应用。Trace 断开时,不要先怀疑追踪后端,而应先定位上下文在哪个边界丢失。
排查顺序可以这样拆:
- 入口请求:确认入口 Trace 是否正常生成,并能关联到生产端业务动作。
- 生产端发送:检查发送消息时是否把上下文写入消息属性或 header。
- 队列传递:确认中间件、代理、网关或桥接组件没有清理这些属性。
- 消费端提取:检查消费者是否从消息属性中恢复上下文,而不是新建根 Trace。
- 日志关联:确认生产端和消费端日志都能写入同一业务键、trace_id 或消息 ID。
这五步能把“看不到完整链路”拆成具体断点:是没有注入、注入后被清理、消费端没有提取,还是日志和 Trace 没有关联。
生产端要把上下文写进消息属性
在同步调用中,Trace 上下文通常通过 HTTP header 或 gRPC metadata 传播。进入消息队列后,请求已经不再是直接调用,下游消费者无法自动从原始 HTTP 请求中读取上下文。
W3C Trace Context 定义了 `traceparent` 和 `tracestate` 这类传播字段,用于让不同系统在边界之间传递 Trace 上下文,具体格式以 W3C Trace Context 为准。OpenTelemetry 也将 context propagation 作为跨进程关联 Trace 的基础机制,见 OpenTelemetry Context Propagation。
在消息场景中,生产端应在发送消息时把上下文写入消息属性、header 或 metadata,而不是只写在日志正文里。
| 边界 | 应保留的信息 | 常见问题 |
|---|---|---|
| 生产端发送 | traceparent、tracestate、业务键、消息 ID | 只生成业务日志,没有注入消息属性 |
| 消息系统 | 消息属性、topic、partition、offset、重试次数 | 代理或桥接组件清理 header |
| 消费端处理 | 父上下文、consumer span、错误状态 | 消费端新建根 Trace,无法关联生产端 |
| 日志系统 | trace_id、span_id、message_id、order_id | 日志字段名不统一,无法反查 |
消息属性要有稳定字段名
如果消息中间件不支持标准 header,也应使用稳定的消息属性字段保存传播信息,并在消费者侧明确读取这些字段。
消费端 Span 不应简单复用生产端 Span
异步消费不是生产端调用的同步延续。消费者处理消息时,应从消息属性中恢复父上下文,然后创建新的消费端 Span,用它记录反序列化、业务处理、下游调用、确认提交或失败重试。

图2:异步消息上下文注入与提取流程
消费端 Span 至少要表达:
- 消费来源:topic、queue、partition、offset 或消息 key。
- 处理动作:消费、反序列化、业务校验、写库、调用下游服务。
- 处理结果:成功、失败、重试、丢弃、进入死信队列。
- 关联字段:trace_id、message_id、业务订单号或任务号。
- 耗时边界:排队等待时间和实际处理时间最好分开记录。
OpenTelemetry 的语义约定中包含 messaging 相关属性,用于描述消息系统、目的地、操作类型和消息标识,具体字段应以 OpenTelemetry Semantic Conventions for Messaging 为准。不要为了追踪方便把完整消息体、Token、手机号或身份证号写入 Span 属性。
重试和死信队列要单独治理
异步链路最容易混乱的地方,是重试。一次业务事件可能被投递多次、消费多次,甚至进入死信队列后再人工回放。如果所有重试都复用同一段日志或只看最终状态,排障时会分不清第一次失败、重试失败和补偿成功之间的关系。
建议把重试链路拆成三类证据:
- 原始消息证据:消息 ID、业务键、首次生产时间和生产端 Trace。
- 每次消费证据:消费次数、消费者实例、错误类型、处理耗时和提交结果。
- 最终归宿证据:成功确认、继续重试、丢弃、死信或人工补偿。
这样做的目标不是把所有事件塞进同一个超长 Trace,而是让同一业务事件的多次处理可以被串联。对于高风险业务,可以在 Trace 之外建立事件审计表或消息处理记录,避免追踪后端采样后丢失关键复盘信息。
不要把消息 ID 当作 TraceID 的替代品
消息 ID 说明“是哪条消息”,TraceID 说明“属于哪条调用链”。两者都需要,但不能互相替代。只保存消息 ID,能找到消息投递和消费记录,却很难还原入口请求、生产端依赖和下游调用;只保存 TraceID,则可能无法区分同一 Trace 下的多条消息。
一套实用的断链排查清单
当异步 Trace 断开时,可以按“生产、传输、消费、关联、复盘”五个层面检查。
上线和排障时至少检查:
- 生产端注入:发送消息前是否能读取当前上下文,并写入消息属性。
- 字段保留:消息中间件、桥接器、代理或 SDK 是否保留传播字段。
- 消费端提取:消费者是否从消息属性恢复父上下文,而不是默认新建根 Trace。
- Span 命名:生产、发送、消费、处理、确认等动作是否能区分。
- 错误状态:消费失败、重试和死信是否记录到 Span status、event 或日志字段。
- 日志关联:生产端、消费者和下游服务日志是否都包含 trace_id 与业务键。
- 采样影响:是否因为采样策略过低,导致关键异常 Trace 未被保留。

图3:异步链路追踪断链排查清单
如果这些检查都通过,但追踪后端仍看不到完整链路,就要继续看 Collector、采样、导出和后端索引。特别是尾采样、字段过滤和多后端路由,可能会让生产端 Trace 与消费端 Trace 被写入不同后端或不同租户。
小结
异步链路追踪的关键,是把同步入口、消息生产、队列投递、消费者处理、重试和日志关联放在同一套上下文治理里。生产端负责注入上下文,消息系统负责保留必要属性,消费端负责恢复上下文并创建新的处理 Span,日志系统负责用 trace_id、message_id 和业务键支撑反查。
只要把断链位置拆清楚,异步 Trace 排查就不会停留在“看不到链路”的笼统判断,而能具体定位到注入、传输、提取、采样或日志关联中的某一个环节。
参考资料
- W3C Trace Context
- OpenTelemetry Context Propagation
- OpenTelemetry Semantic Conventions for Messaging
常见问题
1. 异步链路追踪一定要用 OpenTelemetry 吗?
不一定,但需要有一致的上下文传播机制。OpenTelemetry 的优势是提供统一语义、SDK 和 Collector 生态;如果使用自研方案,也要保证生产端注入、队列保留、消费端提取和日志关联这些环节完整。
2. 消息队列不支持 traceparent 怎么办?
可以用消息属性、header、metadata 或业务扩展字段保存传播信息,但字段名要稳定,生产端和消费端要统一约定。不要把上下文只写在日志文本里,否则消费者无法自动恢复父上下文。
3. 重试消息应该保持同一个 TraceID 吗?
通常应能关联到原始业务事件和入口 Trace,但每次消费尝试要有独立的处理 Span 或事件记录。这样既能看出同一消息的多次处理,也能区分首次失败、重试失败和最终成功。
4. 如何判断异步 Trace 是采样丢失还是上下文断开?
先看生产端和消费端是否都生成 Trace,再看二者是否共享父子关系或相同业务键。如果消费端完全新建根 Trace,多半是上下文没有提取;如果关键错误请求没有样本,则需要检查采样策略和 Collector 管道。