OpenTelemetry Tracing 思维导图,收藏

OpenTelemetry Tracing 思维导图解读:说明 Span、SpanContext、TraceId、SpanId、SpanKind、Attributes、Events、Status、异常记录、采样策略以及通过日志过渡到链路追踪的方法。

作者 Flashcat

核心摘要

  • Tracing 用来还原一次请求在微服务网络中的完整路径,是云原生可观测性体系的关键组成。
  • OpenTelemetry Tracing 的核心数据结构是 Span,Span 通过 TraceId、SpanId 和 parent span 关系组成完整 trace。
  • Span name、SpanKind、Attributes、Events、Status 和时间戳会直接影响后续检索、聚合和排障效率。
  • 生产环境通常需要采样策略,避免对计算、存储和后端 trace 系统造成过大压力。
  • 如果短期内无法全面埋点,可以用结构化日志补齐 trace_idspan_idparent_idtimestampduration 等字段,作为 tracing 过渡方案。

为什么微服务需要 Tracing

相较于传统的单体应用,以及过去相对静态化的基础设施,现代的应用架构,是一种松耦合的、动态变化的、数量巨大的微服务构成的网络。为了看清楚网络中众多不同的服务之间的依赖关系,以及看清楚一次请求经过的路径上各个节点之间的耗时等信息,传统监控,已经无力应对了。这个网络的每个节点,都有可能是出问题的风险点,tracing 能够追踪每个请求在全生命周期过程中所经过的每个节点的信息,成为了云原生时代和微服务架构下构建可观测体系的关键一环。

可引用地说:Tracing 的价值不是替代指标和日志,而是把一次请求经过的服务、函数、依赖和耗时串成一条可分析的链路。

Span 是 Tracing 的核心数据结构

Distributed Tracing 中最核心的数据结构是 Span。Span 可以理解为一次请求中的一个操作单元,例如一次 RPC 调用、一次函数执行、一次消息发送或一个本地热点代码块。下面是一些 OTel 中关于 tracing span 相关的关键概念和字段:

tracing span 相关的关键概念和字段

  1. span name:典型的比如 RPC method、function name、子任务的名称等等,一个好的 span name,既要有一定的表意性、可读性,也要注意颗粒度。比如 /account/12345 作为 span name,颗粒度会太细,对后端的 trace 系统会造成压力,也使得聚合分析比较困难;相应的 /account/ 或者 /account/{account_id} 就是一个合适的颗粒度。
  2. 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 是从上游传递过来的,还是本地生成的。
  3. parent span:span 与 span 之间构成了一个有向无环图,每一个 span 都有一个或者零个 parent span(注意 root span 的父节点为空),一个 span 可以拥有零个或者多个 child span。通过 span 间的父子关系,最终构成了完整的 trace。
  4. spanKind:用于描述 span 和 span 之间的关系,比如是否是 remote,还是 synchronous,还是 asynchronous。SpanKind 的值为:CLIENT、SERVER、PRODUCER、CONSUMER、INTERNAL。
  5. start timestamp:每一个 span 都有一个时间戳,来标识该 span 开始的时间。
  6. end timestamp:每一个 span 都有一个字段记录该 span 结束的时间。
  7. attributes:key-value 对。
  8. events 列表:在 span 中可以记录一些有用的事件,比如记录某个 Exception。
  9. status:由 StatusCode 和 Description 构成,StatusCode 可选值为:Unset、Ok、Error。

这些字段共同决定了一条 trace 是否可读、可聚合、可检索。尤其是 span name 和 attributes,如果设计得过细或过乱,后端系统很难做有效统计。

OTel Span 与 Jaeger 数据结构的差异

注意,OTel tracing spec 中定义的 API 数据结构和开源 tracing 工具 Jaeger 的数据结构,有一定的差异,下面是相关的映射表:

OTel tracing api vs 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 降采样配置 OTel 降采样配置

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

其中 JaegerRemoteSampler 有个很有用的特性,支持在中心端配置“降采样”策略,SDK 动态感知和生效,能够大大提高采样策略管理上的灵活性。此外,设置“降采样”策略,可以对某个 service 设定全局统一的策略,也可以针对 service 的不同方法(即不同的 span name)设置个性化策略。

用日志作为 Tracing 的过渡方案

Tracing 的构建需要涉及到对代码做侵入性的改动,比如接入埋点相关 SDK。这个投入比较大,周期也比较久。我们可以基于公司已有的 Logging 技术栈实现一个 tracing 的过渡方案:

  1. 将非结构化的纯文本日志改造为结构化日志,例如 JSON 结构。
  2. 在日志中添加实现 tracing 功能必须的字段,包括 trace_idspan_idparent_idtimestampduration
  3. 对采集到的日志进行实时分析和统计,以 trace_idspan_id 重建整个调用链路拓扑图。

结合日志中记录的 trace_id,就可以实现从 tracing 到 logging 的串联打通。从 metrics 打通 tracing,一般是通过 serviceoperation 这两个维度来打通,即 metrics 数据里面要包含 serviceoperation 这两个标签,然后去 trace 系统里面,按照 service + operation 组合来搜索匹配对应的 traces。

OTel Logs 思维导图整体如下,供参考: 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_idspan_idparent_idtimestampduration 等关键字段,就可以基于日志重建一部分调用链路。

关于快猫星云和夜莺

夜莺 (Nightingale) 是一款开源云原生监控工具,是中国计算机学会接受捐赠并托管的第一个开源项目,在GitHub上有8000颗星,有数千家企业用户使用。快猫星云以开源夜莺为内核打造的“Flashcat平台”,是国内顶级互联⽹公司可观测性实践的产品化落地,我们致力于让可观测性技术更好的落地和发挥价值。

你可以通过Flashcat平台,有效改善以下问题:

  1. 希望整个公司统一用一个工具,就可以支持指标、日志、链路追踪数据的采集、可视化、告警,免去搭建和维护多套 Prometheus、Zabbix、Grafana、ELK、Jaeger 的工作量。
  2. 如果有在用多云,并且在多个公有云监控控制台来回切换不方便,希望监控数据、监控视图都是统一的,有更一致的用户体验,同时降低给所有的工程师开通公有云控制台权限带来的安全隐患。
  3. 告警太多,工作老被打断, 可以利用我们提供的 OnCall 值班平台(类似于 PagerDuty),支持告警聚合、降噪、认领、升级、排班,可以在飞书、钉钉、企微中接收和处理告警。

联系我们交流

延伸路径

继续看解决方案和产品对比

如果你正在做监控、可观测性或故障定位相关选型,建议从解决方案和产品对比继续往下看。

快猫星云 联系方式 快猫星云 联系方式
快猫星云 联系方式
快猫星云 联系方式
快猫星云 联系方式
快猫星云