LabHub
学习 学习路径 课程

OTCA — OpenTelemetry 认证助理

trace ID存在,跨度为什么不见了

在 LabHub 中继续学习

一句话总结

span 有标识符、记录了内容、已传给 exporter,是三个不同事实。把三者分开确认,才能用观测而非猜测,缩小“已加入计量代码却看不到数据”的问题范围。

概念图: 一句话总结 · 为什么需要它 · 工作原理 · API、SDK、provider、processor、exporter

为什么需要它

订单 API 加入了追踪。开发者在日志中找到了 trace ID,代码中也有 start_span,但观测页面什么都没有。此时重启 Collector,只是在没有证据时随便选择众多原因之一。实际可能是 provider 没连接 processor、sampler 丢弃 span、span 未结束,或数据已到 exporter 却被接收服务器拒绝。

本模块处理这些边界中的 SDK 内部。它不像前面的实验那样手工构造 OTLP JSON,而是把官方 Python SDK 创建的 span 发送给真实 processor 和 exporter,并同时查看 exporter 前可见的信息与实际 HTTP 接收结果。程序无错误退出,并不能证明数据已经送达。

工作原理

API、SDK、provider、processor、exporter

应用的计量代码通过 API 创建 span。SDK 的 TracerProvider 拥有 sampler、resource、processor 等运行配置。取得 tracer 并不会自动完成传输路径;还必须决定由哪个 processor 把结束的 span 交给哪个 exporter。第一个实验故意留空这一连接。

from opentelemetry.sdk.trace.export import SimpleSpanProcessor

provider.add_span_processor(SimpleSpanProcessor(exporter))
tracer = provider.get_tracer("orders.instrumentation")

这里 tracer 的名称用于区分计量库的 scope,不是定义服务名称的 service.name。两者可以碰巧使用同一字符串,职责却不会相同。从一开始区分取值,在多个库共同计量一个服务时也能识别来源。

记录与采样的三种组合

下表是本实验中官方 SDK 与默认 export processor 的观测结果。processor 观测指 span 结束后的调用。

决策 是否记录 sampled 位 到达 processor 到达 exporter
DROP 关闭
RECORD_ONLY 关闭
RECORD_AND_SAMPLE 开启

RECORD_ONLY 尤其重要。“记录”很容易被误读为也会传输,但实验中只有 processor 的结束观测,exporter 的 span 列表为空。可把“在内存中观测信息”和“限制远程发送量”分开。不要只背表格,应修改学员代码中的一个决策,运行后观察各列表如何增减。

即使采样丢弃内容,也可能仍有有效 span context。因此,日志中的 trace ID 只是搜索线索,不保证已存储 span。反之,span 结束后 is_recording 变为 false,也不表示它从一开始就是 DROP。只有记录观测时点,才能区分二者。

ParentBased 的 root 不是整条 trace 的开关

实验策略是:没有父级的请求不记录;有父级则遵循父级 sampled 决策。ParentBased(root=ALWAYS_OFF) 正好表达该策略。即使 root 关闭,来自 sampled 远程父级的子 span 仍会记录。把 root 选项理解成“关闭所有 span”,会把正常子 span 误诊为缺失。

同样,ParentBased(root=ALWAYS_ON) 也不会自动开启 unsampled 父级的子 span。root sampler 只决定没有父级时的行为。远程/本地父级、sampled/unsampled 组合各有分支,默认遵循父级决策。本任务会执行远程两种与本地两种情况,避免把偶然对一个 header 正确的实现当成完整策略。

父级关闭后,子级是否绝对无法开启

不是。预实验中,ALWAYS_ON 和显式修改后的远程父级策略,会创建与 unsampled 父级具有同一 trace ID 的 sampled 子 span。遵循父级决策是一种 sampler 策略,标识符本身不会禁止子级记录。Python 官方 sampling API 也区分 always_on 与 parentbased_always_on。

但这不表示已经丢弃的父级内容被恢复。父级未记录的属性、事件和时间区间仍然不存在。能够观察一个子区间,与整条请求 trace 恢复完整,必须区分。若现场认为“强制打开采样后就能看到全部内容”,会开始另一种误诊。

现场会遇到的情况

设想一个平时只采集部分 root 流量的服务收到外部合作方请求。合作方发送 sampled context 与未发送 sampled context 的情况,不能仅靠同一个 root 比率解释。应依次确认请求 context 是否有效、是否被解释为远程、选定 sampler 是否遵循父级决策。

判断原因是否为 SDK 采样时,比起增加 Collector 日志,比较 SDK processor 前后的数量可能更快。开始和结束观测都没有,就检查创建路径与采样;有结束观测却没有导出,就检查 sampled 与 processor 连接;已确认导出,才移动到传输边界。逐格移动观测位置,可以减少无关基础设施变更。

实验数据是合成订单,不含真实客户信息。生产环境也不应为了错误调查,无条件把原始订单、认证 header 或个人信息放进 span 属性。先考虑能否只用必要的最小标识符和状态区分处理路径。

接下来确认什么

下一篇理论会区分异常事件与 span 状态、span 结束与 flush、接收与存储的边界。之后实验要求直接修改符合说明的 Python 函数;评分依据是真实 SDK 观测结果,而不是能让指示灯变绿的字符串。

官方依据:Tracing SDKPython sampling API