创建的跨度在哪里消失了
目标
修正真实 OpenTelemetry Python SDK 代码,通过观察区分 span 消失在创建、采样、 结束还是传输边界。完成 7 个连接函数和一份综合报告。
为什么重要
即使存在 trace ID 且 flush 返回 True,也不代表目标 span 已经被接收。 本实验使用真实 SDK 1.44.0 和官方 OTLP/HTTP exporter。exporter 会把 protobuf 发送到实验容器内的环回接收器。接收器在部分运行中会故意返回 HTTP 400, 这是为了复现错误而设置的条件。
不使用 Collector、Jaeger 或外部存储。本实验验证实验接收器是否接受数据, 不能扩展解释为已经完成持久化存储或生产部署。SDK 和依赖项已预装在专用环境中, 无需 API key 或联网安装。
准备与运行
工作目录是 /root/otca-sdk。starters 中有 8 个语法正确但行为错误的文件。 各步骤的文件格式示例也是相同的起始代码。请把相应文件复制到工作目录后修改。 例如,第一个文件按以下方式开始。
cd /root/otca-sdk
cp starters/wiring.py wiring.py
/opt/otel-lab/bin/python /opt/app/otca_sdk/runner.py run 1
更改 run 后面的数字,可以查看对应步骤的真实 SDK 观察结果和失败条件。 不要修改 SDK 内部类或评分器,只修改给定函数的代码。 评分会运行副本,不会修改当前答案文件。只要实际行为正确,语义相同的代码也可通过。 普通评分最长 8 秒,综合代码总预算为 50 秒;无限循环和输出泛滥会判定失败。
步骤
- /root/otca-sdk/wiring.py——修正 wiring.py 的 configure(provider, exporter),让给定 provider 中结束的 checkout span 到达 exporter。不要更改全局 provider。
- /root/otca-sdk/decisions.py——decisions.py 的 sampler(mode) 返回 SDK Sampler。让 drop、record、sample 分别满足 DROP、RECORD_ONLY、RECORD_AND_SAMPLE 的行为。
- /root/otca-sdk/parents.py——修正 parents.py 的 parent_sampler():丢弃没有父级的 root,而远程和本地子 span 遵循父级的 sampled 决策。保持父级 trace ID 与 span ID 的关联。
- /root/otca-sdk/errors.py——errors.py 的 record_failure(span, error) 接收已经捕获的订单验证异常。记录原始异常的 exception 事件,并把 span 状态设为 ERROR。
- /root/otca-sdk/lifecycle.py——修正 lifecycle.py 的 finish(span, provider):先结束 span,再调用 force_flush,并返回其返回值。函数返回前,实验接收器必须接受一个 checkout span。
- /root/otca-sdk/delivery.py——delivery.py 的 delivered(observation) 返回 boolean。结合 flush、exporter_results、accepted_spans 进行判断:只有 HTTP 200 接受时返回 True;HTTP 400 拒绝和未结束 span 时返回 False。
- /root/otca-sdk/identity.py——identity.py 的 resource(service_name) 返回 SDK Resource。把传入的服务名称设置为 service.name 资源属性。检查 checkout-api 和 returns-api 两种情况下实际接收的 span。
- /root/otca-sdk/report.json——根据理论和实际观察判断 report.json 中的八个 boolean 假设。使用 JSON boolean,而不是字符串或数字;前七个步骤的代码也必须全部正常工作。
参考
- 采样步骤中的四个数字依次表示:是否记录、sampled、processor 的结束观察结果、exporter 的 span 数量。
- 父级策略会同时测试 root、远程 sampled/unsampled、以及本地 sampled/unsampled。
- 传入 delivered 的 observation 中,flush 是 SDK 返回值,exporter_results 是 exporter 返回结果列表, accepted_spans 是实验接收器通过 HTTP 200 接受的 span 数量。
- 报告使用八个键:record_only_recording, record_only_exported, parentbased_respects_unsampled_parent, always_on_can_sample_child, exception_event_sets_status, flush_ends_open_span, http400_flush_can_be_true, receiver_acceptance_proves_storage。
- 前置步骤只会补齐尚不存在的旧答案,不会覆盖已编写的部分答案或当前步骤答案。
- Pod 是临时环境。请在会话结束前另行保存所需代码和观察结果。
把 span 连接到 exporter
/root/otca-sdk/wiring.py:修正 wiring.py 的 configure(provider, exporter),让给定 provider 中结束的 checkout span 到达 exporter。不要更改全局 provider。
获取 tracer 与连接传输路径是两回事。请确认应该向 provider 注册哪个 processor。
分开记录与采样
/root/otca-sdk/decisions.py:decisions.py 的 sampler(mode) 返回 SDK Sampler。让 drop、record、sample 分别满足 DROP、RECORD_ONLY、RECORD_AND_SAMPLE 的行为。
请同时比较是否记录、sampled 位、processor 观察结果和 exporter 观察结果四个值。
修正父级策略的适用范围
/root/otca-sdk/parents.py:修正 parents.py 的 parent_sampler():丢弃没有父级的 root,而远程和本地子 span 遵循父级的 sampled 决策。保持父级 trace ID 与 span ID 的关联。
单独一个 AlwaysOff 与 ParentBased 的 root=AlwaysOff 并不相同。请区分四种父级情况与 root。
把已捕获异常归类为错误
/root/otca-sdk/errors.py:errors.py 的 record_failure(span, error) 接收已经捕获的订单验证异常。记录原始异常的 exception 事件,并把 span 状态设为 ERROR。
请查看调用 record_exception 后的 status。事件与最终业务状态并不是同一个字段。
结束打开的 span 并导出
/root/otca-sdk/lifecycle.py:修正 lifecycle.py 的 finish(span, provider):先结束 span,再调用 force_flush,并返回其返回值。函数返回前,实验接收器必须接受一个 checkout span。
如果接收数为 0 而 flush 为 True,请确认 span 是否仍处于打开状态。评分器的清理行为不计为学生成功。
修正只相信返回值的判定
/root/otca-sdk/delivery.py:delivery.py 的 delivered(observation) 返回 boolean。结合 flush、exporter_results、accepted_spans 进行判断:只有 HTTP 200 接受时返回 True;HTTP 400 拒绝和未结束 span 时返回 False。
received 中存在正文并不代表已被接受。请检查 exporter 结果和 accepted_spans。
关联服务与当前请求
/root/otca-sdk/identity.py:identity.py 的 resource(service_name) 返回 SDK Resource。把传入的服务名称设置为 service.name 资源属性。检查 checkout-api 和 returns-api 两种情况下实际接收的 span。
请区分 tracer 名称、span 的普通属性与 Resource。不要把一个示例服务名称硬编码到代码中。
综合判断 span 消失的边界
/root/otca-sdk/report.json:根据理论和实际观察判断 report.json 中的八个 boolean 假设。使用 JSON boolean,而不是字符串或数字;前七个步骤的代码也必须全部正常工作。
请区分已经证明的是记录、采样、结束、传输、接受还是存储边界。如果只有报告正确而代码错误,综合验证仍会失败。