同样创建一个 Span,为什么调用方看到 isRecording() 为真,内存 exporter 却没有收到它?把“记录”“sampled 位”“处理器回调”“最终导出”合并成一个布尔值,容易把采样策略误判成网络故障。本篇用三个固定决策把这四道边界拆开。

决策在哪里发生

Sampler.shouldSample 的输入含父 Context、Trace ID、Span 名称、Kind、Attributes 和 Links。调用者是创建 Span 的业务线程,而不是后台 exporter。其返回值 SamplingResult 包含 DROP、RECORD_ONLY、RECORD_AND_SAMPLE 三种决策;此处用按 Span 名选择决策的教学 Sampler,不是 Java SDK 的默认采样配置。

决策 isRecording() sampled 位 本篇 onStart/onEnd 默认 Simple 导出
DROP false false 均无 无
RECORD_ONLY true false 均有 无
RECORD_AND_SAMPLE true true 均有 有

静态源码链为 SdkSpanBuilder.startSpan → shouldSample → 设置 flags → DROP 返回不记录的包装 Span,其他决策进入 SdkSpan.startSpan。isRecording 与 isSampled 分支 明确区别 RECORD_ONLY:它记录但不设置 sampled 位。SdkSpan.startSpan 触发起始处理器回调,end 再触发结束回调。默认 SimpleSpanProcessor.create 令 sampled=true,onEnd 过滤未采样 Span。上游 SdkSpanBuilderTest 单测分别检查记录和 sampled 位;上游 SimpleSpanProcessorTest 覆盖未采样不导出。上游 RECORD_ONLY 的相关旧测试区域是注释掉的,不能把它称为该路径运行证据。

本地逐层断言

Lab08Test 使用三个独立根 Span,分别返回一个采样决策;计数处理器记录回调,再以 SimpleSpanProcessor.create(InMemorySpanExporter) 观察实际导出集合。运行 JAVA_HOME=/tmp/otel-20260930/jdk-extract/usr/lib/jvm/java-21-openjdk-amd64 ./mvnw -q -pl sdk-labs -Dtest=Lab08Test test,一行原始摘要:

1
LAB08 drop=not-recording recordOnly=callbacks-unsampled sampled=exported:1

断言的不只是这行文字:DROP 不记录且无回调,RECORD_ONLY 记录并进入自定义处理器但 sampled 位为 false,RECORD_AND_SAMPLE 同时记录和设置 sampled 位;三个 Span 中只有最后一个出现在本地内存导出列表。失败边界在 Simple 的过滤条件,而不是 HTTP 传输:实验尚未发送 OTLP。更换处理器或 exporter 的选择策略之后,不能无条件把表中最后一列推广成所有 SDK 导出链的行为。

常见误解与练习

“Span 的 sampled 位为 false,所以它不可能触发任何处理器”不成立:RECORD_ONLY 已在本篇触发回调。“创建了 Span 就一定能在 exporter 找到”也不成立:DROP 不记录,RECORD_ONLY 被默认 Simple 过滤。上游代码中 sampled flag 并不是一个后续能被 exporter 任意补回的布尔属性;它由创建时的决策写入 SpanContext。

练习一。 在 Lab08Test 将 SimpleSpanProcessor.create 替换为另一个可观察的自定义处理器,分别计数 onEnd 和实际 export 调用。解释为什么 RECORD_ONLY 可以进入 onEnd 却不一定进入 exporter;不要修改主线默认行为的结论。

练习二。 把教学 Sampler 的 RECORD_ONLY 分支改为 DROP,重跑测试并观察第一个失败断言。根据 SdkSpanBuilder.startSpan 的返回路径分析为什么仅改变 exporter 不足以恢复 DROP 的属性与事件。

导航与参考资料

06 HTTP 传播 · 07 Baggage 与 Link · 当前篇:08 头部采样。下一篇:09 SpanProcessor(待写)。

参考资料:上述固定 SHA 的 OpenTelemetry Java SDK Sampler、SamplingResult、SdkSpanBuilder、SimpleSpanProcessor 与上游测试;本地记录 examples/opentelemetry-java/evidence/08/RUN.md。