全链路追踪怎么做?traceId、span 与日志体系如何打通?
结论先行:全链路追踪把一次请求的跨服务调用组织成一棵树:traceId 标识整条链路,spanId 标识其中一次调用(含父 span),按时间与父子关系还原调用瀑布。价值一半在追踪数据,另一半在与日志打通——让 traceId 贯穿日志,出问题能一条链拉出全部日志。
一、核心概念
| 概念 | 含义 | 例子 |
|---|---|---|
| trace | 一次完整请求 | 用户下单的整条链路 |
| traceId | 链路唯一 id | 全局透传,日志关联键 |
| span | 一次 RPC/DB/本地调用 | 网关到订单服务的调用 |
| spanId / parentSpanId | 本调用与父调用 | 组成树形结构 |
trace: 6d6e…
├─ span A 网关 -> 订单服务 5ms
│ ├─ span B 订单 -> DB 2ms
│ └─ span C 订单 -> MQ 1ms
└─ span D 库存服务消费事件 4ms
二、traceId 怎么传
- 入口(网关/入口服务)生成 traceId,写入约定 Header(如 X-Trace-Id)随 HTTP/RPC/MQ 透传。
- 中间件统一拦截:从 Header 取或新建 traceId,放入上下文与日志 MDC。
- 常见的坑:异步线程池、MQ 消费要手动传递上下文,否则 trace 断链。
三、与日志体系打通
// 日志里带上 traceId(如 logback 的 MDC)
MDC.put("traceId", traceId);
log.info("扣减库存开始");
- 约定:所有应用日志行都打印 traceId,采集到 ELK/Loki 后可按 traceId 聚合一次请求的全部日志。
- 自查标准:没有 traceId 的日志行等于离线日志,无法串联,要在埋点规范上卡住。
四、方案与采样
- 开源选型:SkyWalking(自研探针 + UI)、Zipkin/Jaeger(OpenTelemetry 生态)。
- 采样策略:高价值接口全量采样 + 普通接口按比例采样(如 10%)+ 错误链路强制采样。
常见追问与记忆点
- 追问:追踪系统本身挂了会影响业务吗?探针必须旁路:异步上报、失败静默,绝不阻塞主链路。
- 追问:怎么快速定位慢在哪?看 trace 瀑布里耗时占比最大的 span,结合日志与指标下钻。
- 记忆点:traceId 贯穿链路、span 组织调用树;追踪的价值 = 链路图 + 全链路可检索日志。