把 -javaagent 的 JAR 从 1.31.0 换成 1.32.0,应用仍能拿到非空的 GlobalOpenTelemetry,并不等于自定义扩展被加载、默认采样保持不变或后端收到数据。版本回归至少要把发行 JAR、应用 API classpath、Agent 内部 SDK 与扩展 JAR 分开核对,然后在独立 JVM 里重复同一套断言。

这次固定两对上游源码:旧 Java SDK SHA c25c0a0ee0da01ab2f74ba83052d1c249ed57020 / instrumentation SHA 97d87f3f3b2bc61aa396d71a98387a1e857618af;新 SDK SHA ff2e1638caee86120b0ad0e3bd3c2af68d07ae4e / instrumentation SHA a36175cb65e8fe48a4da388d1c87288701e1b7ad。Maven Agent 发行 JAR 1.31.0/1.32.0 及本仓库 extension-lab JAR 的实际 SHA-256 见 writing-plans/opentelemetry-java/VERSIONS.md。应用测试 classpath 保持 1.31.0,两版 Agent 分别在新的 JDK 21.0.12 进程自带 SDK 运行,不创建第二个全局 provider。本篇“自定义发行组合”是 Agent 原版发行 JAR 加已有扩展 JAR,并非重新打包发布一个新 Agent。

两份 SHA:哪些没变,哪些需另验

旧 GlobalOpenTelemetry.java L54–83 与新 GlobalOpenTelemetry.java L54–89 在这里都先读进程内 global,缺失时才走自动配置/退回 noop;新版本对两个静态字段增加 lint suppress,并不证明初始化规则改变。旧 AutoConfiguredOpenTelemetrySdkBuilder.java L332–357 直接走属性配置;新 AutoConfiguredOpenTelemetrySdkBuilder.java L338–356、L432–465 先检查 OTEL_CONFIG_FILE 并尝试从文件配置;本实验没有设置它,不能声称已验证文件解析成功。

两版 TracerProviderConfiguration.java 旧 L32–50 与 新 L32–50 均以 parentbased_always_on 作为未配置 sampler 的默认。两版 Agent 的 OpenTelemetryInstaller.java 旧 L24–46 / 新 L24–46 将 autoconfigure 的结果设为 global、让 logs exporter 默认 none。这不是说这次跑了默认 Logs 出口:测试显式设置 OTEL_LOGS_EXPORTER=none 和 OTEL_METRICS_EXPORTER=none。新 AgentInstaller.java L125–132、L203–212 还初始化资源属性 holder,拷贝属性的列表不再包含旧版的一项 HTTP forwarded-scheme 选项;没有在本实验验证两项的运行差异。新源码 dependencyManagement/build.gradle.kts L11 声明 SDK 1.32.0,发行 JAR manifest 写明 Premain-Class。上游 OpenTelemetryInstallerTest.groovy 新 L23–40 断言安装与日志默认值;这里仅静态阅读,没有运行上游测试。

同一套应用、四个进程

本地 examples/opentelemetry-java/sdk-labs/src/test/java/org/example/otel/E05Test.java 在临时目录编译相同的 CustomLibrary.execute(String),然后分别给 1.31/1.32 Agent 挂同一个自定义扩展 JAR。E05Probe 只调用 Agent 已安装的全局 API:创建 e05-parent、带 Scope 调用自定义方法、注入 W3C traceparent。每个版本各跑默认 sampler 和显式 always_off + 禁用扩展两个独立子进程,出口统一指向测试 JVM 的 OTLP/HTTP protobuf 教学接收端;不能据此叫作 Collector。

版本与依赖 默认 root / Trace Context 默认可见 Span / 资源字段 显式 always_off + 禁用扩展
Agent 1.31.0 + 本仓库扩展 / 应用 API 1.31.0 recording、valid、sampled 均 true;traceparent 末尾 01 e05-parent 与 CustomLibrary.execute;父 ID 对齐;service.name=unknown_service:java valid true、recording/sampled false;尾 00;无这两条 Span
Agent 1.32.0 + 同一扩展 / 应用 API 仍是 1.31.0 同一组断言通过 同一组断言通过 同一组断言通过
1
2
3
4
5
6
7
var parent = GlobalOpenTelemetry.get().getTracer("e05-upgrade")
.spanBuilder("e05-parent").startSpan();
try (var scope = parent.makeCurrent()) {
library.getClass().getMethod("execute", String.class).invoke(library, "default");
} finally {
parent.end();
}

每一轮的 Agent/扩展文件 SHA-256 在 Java 断言中再次核对。默认组 receiver 确实收到并解析两个有关 Span,且扩展 Span 的 parentSpanId 等于手动 parent 的 spanId;显式组没有有关 Span。显式组同时关闭了采样和扩展,零输出不能单独归因于扩展开关。四个独立进程都退出码 0;运行命令和包含随机 ID 的原始摘要见 examples/opentelemetry-java/evidence/E05/RUN.md。

回归成立的范围

同一应用 API 与本仓库扩展 JAR 在这两个 Agent 发行版本、这一个方法签名和 JDK 21 上通过,不等于自定义扩展对所有 Agent 版本都兼容;也没有重新编译整个项目分别依赖 SDK 1.31 和 1.32。OTEL_CONFIG_FILE、forwarded-scheme 选项及新资源属性 holder 只比过源码,没有运行注入。测试 receiver 不是 Collector/Jaeger;没有验证 Metrics/Logs OTLP、远程存储、性能或生产环境。保留此边界,比“升级无感”更有用。

两道练习

  1. 修改 E05Test 为两个独立反例进程:只关闭扩展但保持默认 sampler、只设置 always_off 但保持扩展;分别断言 receiver Span 集合,不能直接从本篇双开关组推断各自作用。
  2. 使用两版固定 SHA 分别核对 OTEL_CONFIG_FILE 的入口与失败分支,选用确实存在的配置文件解析 artifact,冻结发行 JAR/校验后再运行两版独立 JVM 比较错误/成功;不要将本文的静态分支当成该练习已跑结果。

导航与参考资料