代码里已经调用 GlobalOpenTelemetry.getTracer(...),为什么没有 Trace?入口可以存在,SDK 却未注册。第 00 篇显式组装 SDK;这次把显式、自动配置和根本没有 SDK JAR 的情况放到三个独立进程中,避免把单例的残留状态误认作配置效果。

API 的入口不等于 SDK 的安装

1
2
应用 -> GlobalOpenTelemetry -> [已注册 SDK] -> processor -> exporter
-> [未注册 / 无 SDK] -> noop

公开 GlobalOpenTelemetry.get 首先查看已经注册的全局实例;没有时尝试自动配置,仍不满足条件才设置 noop。这个入口本身不会神奇地创建处理器和发送端。get() 后再 set() 会抛 IllegalStateException:它不是可以随时覆盖的线程本地变量。上游全局初始化的边界测试见 OpenTelemetryTest。

显式分支由应用创建 SdkTracerProvider、processor 和 exporter,交给 OpenTelemetrySdk,在首次使用全局入口前注册。这样依赖和收集链的配置都受应用控制。进程退出时要关闭 provider;本篇用内存 exporter,在关闭前读取列表,因为 exporter 的关闭会清除收集数据。

自动配置和配置优先级

AutoConfiguredOpenTelemetrySdk.builder() 并非“只要加一个依赖就总会自动启用全局 SDK”。本篇显式调用 setResultAsGlobal().build();另一路是 GlobalOpenTelemetry 的反射自动发现,在这个版本还要 otel.java.global-autoconfigure.enabled=true。二者是不同的初始化入口,不能混写。

内部 AutoConfiguredOpenTelemetrySdkBuilder.build 读取配置、构造 resource、provider 与 exporter,并在设置了 setResultAsGlobal 时注册全局对象;出错时关闭已构造的部分组件。这些实现类不是本篇业务代码的依赖目标。属性合并由内部 DefaultConfigProperties.create 负责:默认值 < 环境变量 < Java system properties。上游的 AutoConfiguredOpenTelemetrySdkTest 覆盖 supplier 与全局注册分支;这里另外用跨进程实验核对实际优先级。配置的作用范围限于这个冻结版本和所走的 builder 路径。

三个隔离进程

从工程目录执行 ./mvnw -q -pl sdk-labs -Dtest=Lab01Test test。测试用有期限的 ProcessBuilder 启动三个 JVM,不调用 resetForTest 掩盖应用级单例约束。

进程 classpath / 初始化 断言
explicit SDK + 自建 Simple processor 关闭前内存列表有 1 条;二次注册失败
auto SDK + 自动配置 builder;关闭默认 traces/metrics/logs exporter,添加内存处理器 内存有 1 条;同名 OTEL_SERVICE_NAME=from-env 与 -D 属性冲突时,service.name 为 from-property
none 只包含 API、Context 和测试类,没有 SDK JAR Span 不记录,ID 无效;首次 get() 后注册失败

输出中的 mode=explicit finished-before-shutdown=1、config service.name=from-property (env=from-env) 与 mode=none recording=false validId=false 分别来自三个进程。没有外部 OTLP 接收端;自动配置进程禁用了默认导出,避免在测试里误连工作站的遥测 endpoint。三种设置都不能证明 Agent 的默认设置与其相同。

可迁移的边界

全局实例是应用级一次性决策。先创建 SDK、再注册,最后才让业务使用全局入口;需要多配置对照时使用隔离 JVM。这是对单例依赖的测试隔离原则,不等于建议所有业务都必须使用全局变量。可显式传入 OpenTelemetry 的地方,更容易观察依赖关系。

常见误解。 getTracer() 能调用不代表 Span 一定会记录;AutoConfiguredOpenTelemetrySdk 在 classpath 上也不代表 GlobalOpenTelemetry.get() 自动启用它。otel.sdk.disabled=true 又是独立的配置开关,不能与“完全没有 SDK JAR”混为一谈。

练习一。 把 NoSdkProbe 的 API-only classpath 改成包含 SDK,但不显式注册;在本版默认未启用全局自动配置时比较输出。说明与“没有 SDK JAR”相同的结果为何来自不同条件。

练习二。 在 InitProbe 的自动分支删去 System.setProperty("otel.service.name", ...),再执行 01 测试;预期 resource 从哪里获得服务名?不要在验证前改写原始证据。

系列导航与资料

00 导读与第一条 Trace · 当前篇:01 API、SDK 与初始化。下一篇:02 Resource 与 InstrumentationScope(待写)。

参考资料:Java SDK 文档、上文固定 SHA 的实现和测试(Apache-2.0)、工程中的 Lab01Test / InitProbe / NoSdkProbe。这里的 Java 进程是教学对照,不是上游代码或网络链路测量。