深入 OpenTelemetry 22:哪些 HTTP 方法会进入 Advice
同样是 URLConnection,为什么只观察到 HTTP Span?
URL.openConnection() 可以返回 HTTP 连接,也可以返回 file 连接;方法名或接口像,并不意味着都被增强。为排除“Agent 只要启动就给每次 I/O 建 Span”的误解,本篇用 JDK 21 自带的两个实现作对照:先看模块怎样筛类型和方法,再观察只有 HTTP 请求形成一个 CLIENT Span。
| 步骤 | 决策位置 | 当前测试实例 |
|---|---|---|
| 模块装载 | InstrumentationModule |
名称 http-url-connection |
| 类型匹配 | TypeInstrumentation.typeMatcher() |
HTTP 子类通过;file 子类不通过 |
| 方法匹配 | transform → Byte Buddy Advice |
connect / getInputStream 等;不等于所有方法 |
| 运行入口 | Advice → Instrumenter | 读取连接状态、Span 生命周期;在本地端点核对输出 |
固定源码:模块、类型、方法与运行时门槛
SDK SHA c25c0a0ee0da01ab2f74ba83052d1c249ed57020,instrumentation/Agent 源码 SHA 97d87f3f3b2bc61aa396d71a98387a1e857618af,版本均为 v1.31.0。本节是静态阅读。HttpUrlConnectionInstrumentationModule L15–25 以 @AutoService 注册,返回类型规则;InstrumentationModuleInstaller L143–193 组合类型、classloader 匹配与 transformer。只发现模块不代表每个类都通过规则;外部依赖还要关注模块兼容性条件。
HttpUrlConnectionInstrumentation.typeMatcher L32–42 要求指定包/类名范围,同时继承 java.net.HttpURLConnection;特意排除一个 HTTPS 简单委托类。file.FileURLConnection 不满足继承条件。L45–52 对 connect、getOutputStream、getInputStream 装入主 Advice,对 getResponseCode 装入状态码 Advice;字节码增强是否执行还取决于方法实际被调用。
HttpUrlConnectionAdvice L58–95 以 CallDepth 避免递归,调用 instrumenter().shouldStart,并在 VirtualField 保存连接对应状态;L98–150 的退出 Advice 把 Scope 关闭并决定何时结束。两处 Advice 都标有 suppress = Throwable.class:它抑制的是插桩代码自身抛错对业务的影响,不能解释为业务 I/O 异常永远不发生。上游 HttpUrlConnectionTest L46–80 与 HttpUrlConnectionResponseCodeOnlyTest L21–43 覆盖两种客户端调用方式;没有在本机运行这些测试。
JDK 内置客户端的匹配反例
引入前冻结的 HTTP 客户端是 JDK 21.0.12 内置 HttpURLConnection,不是另一个 Maven artifact;lib/modules SHA-256 9a8e9e8a7aab3a5e010e56c65f13449c17b08e5eede93cd7bb796b7df31c572b,bin/java SHA-256 377196a32c5e4442b604bbf36add1c49cfe8680cd27edd693b60872e58895e9。Agent JAR 同 21 篇,SHA-256 e866de5fa4e4c2c7d076072798fb9ca65b679d9c6be7792b37d4c94e2bc686f7。使用另一个 JDK patch 或外部客户端需要重新核对匹配。
AgentHttpProbe 同时发一个本地 HTTP GET、读取一次 file URL 的 class 文件。Lab22Test 对比两个独立 JVM;采集端是测试 JVM 的 /v1/traces(OTLP/HTTP protobuf 教学端点),不是 Collector。在 examples/opentelemetry-java/ 运行:
1 | |
退出码 0,原始结果:
1 | |
断言还检查实际类分别为 sun.net.www.protocol.http.HttpURLConnection 与 sun.net.www.protocol.file.FileURLConnection、两次 HTTP 服务均收到一次请求。挂 Agent 的测试端点只接收一个 CLIENT Span;file URL 的读取没有额外 Span。进程使用有期限等待,OTLP 接收使用 CountDownLatch;不用固定睡眠。静态规则与本地输出共同支持这两个类的对照,不覆盖全部第三方 HTTP 客户端。进程内教学接收端收到请求,也不意味着真实 Collector 或后端可查。
边界、误解与练习
工程原则是先定位“哪一个类型、哪一个方法、哪一种加载器”,再判断业务代码是否真的走到 Advice;不能仅凭 -javaagent 存在就推断覆盖范围。Span 总数为 1 不能证明无其他未调用的方法被增强;并发、重试、外部库兼容性尚未验证。两道练习:
- 修改
AgentHttpProbe,只调用getResponseCode()再断开,参照上游 ResponseCodeOnly 测试检查是否仍产生一个 CLIENT Span;同时记录 HTTP 服务收到的请求数,解释何时结束。 - 构造一个只读 file URL 的进程,分别带/不带 Agent;为什么两次都没有 Span 还不足以说明 Agent 安装失败?至少给出一个检查全局 SDK 的额外断言。
参考资料:HttpURLConnection 插桩与测试(固定 SHA)、Java SDK(固定 SHA)、实验命令/原始输出见 examples/opentelemetry-java/evidence/22/RUN.md。
导航:21 Agent 启动 · 当前篇:22 字节码匹配与 Advice。
