trace ID存在,跨度为什么不见了
一句话总结
span 有标识符、记录了内容、已传给 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 观测结果,而不是能让指示灯变绿的字符串。