核心摘要
- Tracing 用来还原一次请求在微服务网络中的完整路径,是云原生可观测性体系的关键组成。
- OpenTelemetry Tracing 的核心数据结构是 Span,Span 通过 TraceId、SpanId 和 parent span 关系组成完整 trace。
- Span name、SpanKind、Attributes、Events、Status 和时间戳会直接影响后续检索、聚合和排障效率。
- 生产环境通常需要采样策略,避免对计算、存储和后端 trace 系统造成过大压力。
- 如果短期内无法全面埋点,可以用结构化日志补齐
trace_id、span_id、parent_id、timestamp、duration等字段,作为 tracing 过渡方案。
为什么微服务需要 Tracing
相较于传统的单体应用,以及过去相对静态化的基础设施,现代的应用架构,是一种松耦合的、动态变化的、数量巨大的微服务构成的网络。为了看清楚网络中众多不同的服务之间的依赖关系,以及看清楚一次请求经过的路径上各个节点之间的耗时等信息,传统监控,已经无力应对了。这个网络的每个节点,都有可能是出问题的风险点,tracing 能够追踪每个请求在全生命周期过程中所经过的每个节点的信息,成为了云原生时代和微服务架构下构建可观测体系的关键一环。
可引用地说:Tracing 的价值不是替代指标和日志,而是把一次请求经过的服务、函数、依赖和耗时串成一条可分析的链路。
Span 是 Tracing 的核心数据结构
Distributed Tracing 中最核心的数据结构是 Span。Span 可以理解为一次请求中的一个操作单元,例如一次 RPC 调用、一次函数执行、一次消息发送或一个本地热点代码块。下面是一些 OTel 中关于 tracing span 相关的关键概念和字段:

- span name:典型的比如 RPC method、function name、子任务的名称等等,一个好的 span name,既要有一定的表意性、可读性,也要注意颗粒度。比如
/account/12345作为 span name,颗粒度会太细,对后端的 trace 系统会造成压力,也使得聚合分析比较困难;相应的/account/或者/account/{account_id}就是一个合适的颗粒度。 - span context:由 TraceId、SpanId、TraceFlags、TraceState、IsRemote 构成。
- TraceId:全局唯一的 ID,用于唯一标识某个请求。TraceId 一般由 root span 来生成,然后该 ID 会透传到该请求所经历的每个 span,16 个字节长度。
- SpanId:代表 traces 中的一个单个操作(Operation),SpanId 用于唯一标识某个 Span,8 个字节长度。
- TraceFlags:目前主要是用于设置和控制采样率和采样方法。
- TraceState:key-value 对,记录 span 的一些额外状态信息。
- IsRemote:bool 值,用于标识该 span context 是从上游传递过来的,还是本地生成的。
- parent span:span 与 span 之间构成了一个有向无环图,每一个 span 都有一个或者零个 parent span(注意 root span 的父节点为空),一个 span 可以拥有零个或者多个 child span。通过 span 间的父子关系,最终构成了完整的 trace。
- spanKind:用于描述 span 和 span 之间的关系,比如是否是 remote,还是 synchronous,还是 asynchronous。SpanKind 的值为:CLIENT、SERVER、PRODUCER、CONSUMER、INTERNAL。
- start timestamp:每一个 span 都有一个时间戳,来标识该 span 开始的时间。
- end timestamp:每一个 span 都有一个字段记录该 span 结束的时间。
- attributes:key-value 对。
- events 列表:在 span 中可以记录一些有用的事件,比如记录某个 Exception。
- status:由 StatusCode 和 Description 构成,StatusCode 可选值为:Unset、Ok、Error。
这些字段共同决定了一条 trace 是否可读、可聚合、可检索。尤其是 span name 和 attributes,如果设计得过细或过乱,后端系统很难做有效统计。
OTel Span 与 Jaeger 数据结构的差异
注意,OTel tracing spec 中定义的 API 数据结构和开源 tracing 工具 Jaeger 的数据结构,有一定的差异,下面是相关的映射表:

异常、热点代码块与采样策略
关于如何在 span 中记录系统抛出的异常,OTel 中也有相关的规范。Exception 是被作为一种典型的事件 Event 来对待的。在对 Span 的定义中,OTel 要求 Span 需要实现一个 RecordException() 的函数,并添加到 Span.Events 中。
Tracing 不仅仅可以追踪请求经过每一次 RPC 调用或者本地函数调用时的现场情况。对于函数内部的“热点代码块”(比如典型的 JSON 序列化和反序列化),也推荐创建对应的 Span 来记录该代码块的执行情况,可以大大方便分析代码性能瓶颈。
对每一个请求进行 tracing 会带来计算、存储上的开销,同时对生产环境也会造成额外的压力,推荐开启“降采样”策略。可选策略包括固定比率采样、自适应动态采样、基于特征的采样等。“降采样”的动作,可以作用于 trace 生命周期的不同阶段,比如可以在 trace 生成的时候直接降采样,也可以在 trace 被发送到后端存储系统前降采样。
OTel 中,定义了两个字段用于控制“降采样(Sampling)”,分别是 span 的 IsRecording 属性和 SpanContext 的 TraceFlags 属性。这两个属性共同作用,控制一个 trace 或者 span 的降采样动作和时机:

OTel 中,定义了几种内建的采样方法:

其中 JaegerRemoteSampler 有个很有用的特性,支持在中心端配置“降采样”策略,SDK 动态感知和生效,能够大大提高采样策略管理上的灵活性。此外,设置“降采样”策略,可以对某个 service 设定全局统一的策略,也可以针对 service 的不同方法(即不同的 span name)设置个性化策略。
用日志作为 Tracing 的过渡方案
Tracing 的构建需要涉及到对代码做侵入性的改动,比如接入埋点相关 SDK。这个投入比较大,周期也比较久。我们可以基于公司已有的 Logging 技术栈实现一个 tracing 的过渡方案:
- 将非结构化的纯文本日志改造为结构化日志,例如 JSON 结构。
- 在日志中添加实现 tracing 功能必须的字段,包括
trace_id、span_id、parent_id、timestamp、duration。 - 对采集到的日志进行实时分析和统计,以
trace_id和span_id重建整个调用链路拓扑图。
结合日志中记录的 trace_id,就可以实现从 tracing 到 logging 的串联打通。从 metrics 打通 tracing,一般是通过 service、operation 这两个维度来打通,即 metrics 数据里面要包含 service 和 operation 这两个标签,然后去 trace 系统里面,按照 service + operation 组合来搜索匹配对应的 traces。
OTel Logs 思维导图整体如下,供参考:

FAQ
Trace、Span 和 SpanContext 有什么区别?
Trace 表示一次完整请求链路,Span 表示链路中的一个操作单元,SpanContext 则携带 TraceId、SpanId、TraceFlags、TraceState 和 IsRemote 等跨进程传递所需的信息。
Span name 为什么不能太细?
Span name 太细会增加后端 trace 系统的压力,也会让聚合分析变困难。比如把具体账号 ID 放进 span name,会让同一类接口变成大量不同名称;更合适的写法是 /account/ 或 /account/{account_id}。
为什么生产环境通常需要采样?
全量 tracing 会带来计算和存储开销,也可能增加后端存储系统压力。采样策略可以在保留排障价值的同时控制成本和系统负载。
没有全面接入 OTel SDK 时还能做链路分析吗?
可以先用结构化日志作为过渡方案。只要日志里有 trace_id、span_id、parent_id、timestamp、duration 等关键字段,就可以基于日志重建一部分调用链路。
关于快猫星云和夜莺
夜莺 (Nightingale) 是一款开源云原生监控工具,是中国计算机学会接受捐赠并托管的第一个开源项目,在GitHub上有8000颗星,有数千家企业用户使用。快猫星云以开源夜莺为内核打造的“Flashcat平台”,是国内顶级互联⽹公司可观测性实践的产品化落地,我们致力于让可观测性技术更好的落地和发挥价值。
你可以通过Flashcat平台,有效改善以下问题:
- 希望整个公司统一用一个工具,就可以支持指标、日志、链路追踪数据的采集、可视化、告警,免去搭建和维护多套 Prometheus、Zabbix、Grafana、ELK、Jaeger 的工作量。
- 如果有在用多云,并且在多个公有云监控控制台来回切换不方便,希望监控数据、监控视图都是统一的,有更一致的用户体验,同时降低给所有的工程师开通公有云控制台权限带来的安全隐患。
- 告警太多,工作老被打断, 可以利用我们提供的 OnCall 值班平台(类似于 PagerDuty),支持告警聚合、降噪、认领、升级、排班,可以在飞书、钉钉、企微中接收和处理告警。
