前言
在很多团队里,日志系统和链路追踪系统都已经上线了:
- 日志进了 ELK 或 Loki
- 链路进了 Jaeger 或 Tempo
看起来可观测性很完整,但线上问题一来,大家还是要在两个系统里来回跳:先在日志里搜报错,再按时间戳去链路系统猜是哪条请求。排障效率并没有本质提升。
我在项目里做全链路追踪时,重点不是单纯增加 span,而是让日志和链路形成一个双向可跳转的闭环。本文会讲清楚三个问题:
- 为什么日志和链路天然会割裂
- 我们具体做了什么设计让它们融合
- 实战中怎么用这套体系快速定位问题
一、为什么日志和链路天然会割裂
1. 数据模型不同
日志记录的是离散事件,形态是文本或半结构化文本。链路追踪记录的是调用关系,形态是树状结构。
- 日志回答的是:发生了什么
- 链路回答的是:在哪条调用路径上发生、谁是父调用、耗时在哪里
两个模型天然不同,所以默认情况下很难自动关联。
2. 存储和查询系统不同
日志通常在日志平台,核心能力是全文检索。 链路通常在 tracing 平台,核心能力是拓扑展示、耗时分析。
当它们处于两个系统、两个索引、两个查询入口时,如果没有共同标识,关联只能靠时间戳和人工推断。
3. 上下文默认不会自动共享
最关键的一点:如果日志里没有 TraceID,或者异步边界没有传播 trace 上下文,那么即使 tracing 做得再好,日志仍然是孤岛。
二、我们项目的融合设计:三层打通
我们的目标不是“日志系统和 tracing 系统并存”,而是“查询路径双向可达”。为此做了三层设计。
1) TraceID 全程注入:先解决身份统一
HTTP 入口会优先从标准 traceparent 恢复 trace,上下文中始终携带 TraceID。即使没有上游 trace,也会生成新的 TraceID 写入上下文,并回写响应头,确保日志侧始终有统一身份。
这一步的价值是:
- 任意一条日志都可拿到 TraceID
- 用 TraceID 可反查整条链路
2) 日志写入 Span Event:再解决“看链路就能看关键日志”
我们实现了日志 Hook,把 Warn 及以上级别日志自动写入当前活跃 span 的 event。
效果是:
- 在链路平台打开一个红色 span,不需要切日志平台,就能看到当时的关键错误日志
- span 的状态可与错误日志联动,错误定位更直接
为了控制噪声,Info 与 Debug 级别不写入 span event。
3) 跨异步边界传播:避免 HTTP 与 Worker 断链
项目链路并非纯同步调用,而是:
HTTP -> Asynq 入队 -> Worker 消费 -> 工作流引擎 -> AI 调用
我们在入队前注入 trace 上下文到 payload,Worker 消费时提取恢复。这样 Worker 新建 span 仍然挂在原始 trace 下,日志也延续同一 TraceID。
这一步决定了“全链路”是否名副其实。
三、一个真实可复用的排障流程
以一次工作流执行为例,主干链路通常是:
HTTP root -> workflow.run.prepare -> workflow.run.consume -> workflow.run.execute -> EinoNode -> ai.chat
当出现错误时,有两条高效路径。
路径 A:从日志到链路
- 在日志平台发现错误日志
- 复制日志里的 TraceID
- 到 Jaeger 按 TraceID 查询
- 直接看到完整调用树和慢点/失败点
路径 B:从链路到日志
- 在链路平台发现异常 span
- 打开 span 的 events
- 直接查看挂载的 Warn/Error 日志
- 用日志细节反向定位参数、依赖、数据状态
这就是“日志和链路双向联通”的核心价值。
四、为什么这套设计有效
我总结为三点。
1. 不再依赖时间戳猜测
以前是“同一时间附近可能是这条请求”,现在是“同一个 TraceID 必然属于这条请求”。
2. 异步场景仍然可追
HTTP 返回后,后续 Worker、引擎节点、AI 调用都在同一条 trace 下,真正覆盖完整业务生命周期。
3. 控制成本,不追求无脑全量
- 生产采样不是全量
- 日志 event 只收关键级别
- 高频路径可通过降噪策略减少无效 span
可观测性必须和成本平衡,这点非常关键。
五、落地时的注意事项
如果你也准备做类似融合,我建议优先确保以下事项:
- 先统一 TraceID 生成与传递规则
- 保证日志框架默认输出 TraceID 字段
- 明确哪些日志级别写入 span event
- 必做异步边界传播,不然全链路一定断
- 定义降噪与采样策略,避免追踪系统被高频噪声淹没
六、结语
“有日志 + 有 tracing”不等于“可观测闭环”。
真正的闭环是:
- 日志能通过 TraceID 反查链路
- 链路能直接查看关键日志
- 跨异步边界依旧不断链
当这三件事同时成立时,线上排障体验会发生质变:从跨系统猜测,变成同一上下文下的确定性定位。
如果只留一句话给团队,我会写:
让日志和链路说同一种语言,也就是让它们共享同一个上下文身份,并支持双向跳转。