Go、.NETときて、今回はJavaでのzero-code計装のトライである。
kmuto.hatenablog.com kmuto.hatenablog.com
公式サイトによれば、Java Agentを使う方法と、Spring Boot starterを使う方法がある。
まずはJava Agentで試すことにした。
Java Agentの場合、シンプルにopentelemetry-javaagent.jarをエージェント組み込みするだけでJVMから情報をひっぱってきてくれる。
java -javaagent:path/to/opentelemetry-javaagent.jar -Dotel.service.name=your-service-name -jar myapp.jar
なるほど簡単。引数のほか環境変数、プロパティファイルで指定することもできる。
で、さっそく素から立てやすいWebサーバーとしてsun.net.httpserver.HttpServer
を使って立てたのだが…JVMのメトリックは飛ぶものの、トレースが全然送られない。
サポートライブラリの対象になかった。せやな…。
標準ライブラリ範疇だとJava Http Clientやloggingあたりの対応があるのだが、Webサーバーだと結局KtorかSpringになるようだ。
Javaと一言で表すには大掛かりになってしまうものの、KotlinのKtorで試すことにする。
Kotlinは昔書籍制作で検証するのに使ったくらいで、完全に忘却。とはいえ、公式サイトを見ながら適当にやったらできた。
Project Generatorからktor-sample.zipをダウンロードし、展開する。
src/main/kotlin/Routing.kt
に/500
と/error
のハンドラを追加した。
package com.example import io.ktor.server.application.* import io.ktor.server.response.* import io.ktor.server.routing.* fun Application.configureRouting() { routing { get("/") { call.respondText("Hello World!") } get("/500") { call.respondText("Error", status = io.ktor.http.HttpStatusCode.InternalServerError) } get("/error") { throw RuntimeException("Runtime Error") } } }
ビルドして、正常に動作するか試す。
./gradlew build java -jar build/libs/ktor-sample-all.jar
ポート8080で動いているので、curlから呼び出し。
$ curl http://localhost:8080 Hello World! $ curl http://localhost:8080/error (何も出ないけどサーバー側は例外が出ている) $ curl http://localhost:8080/500 Error
いつものようにOpenTelemetry Collectorを実行しておく。
receivers: otlp: protocols: http: exporters: debug: verbosity: detailed service: pipelines: traces: receivers: [otlp] exporters: [debug]
ではzero-code計装を注入する。
java -javaagent:./opentelemetry-javaagent.jar -Dotel.service.name=kotlin-zerocode -jar build/libs/ktor-sample-all.jar
これで再度curlを試してみると、トレースが出てくる。
2025-01-19T17:50:04.119+0900 info Traces {"kind": "exporter", "data_type": "traces", "name": "debug", "resource spans": 1, "spans": 1} 2025-01-19T17:50:04.119+0900 info ResourceSpans #0 Resource SchemaURL: https://opentelemetry.io/schemas/1.24.0 Resource attributes: -> host.arch: Str(amd64) -> host.name: Str(...) -> os.description: Str(Linux 6.1.0-30-amd64) -> os.type: Str(linux) -> process.command_args: Slice(["/usr/lib/jvm/java-17-openjdk-amd64/bin/java","-javaagent:./opentelemetry-javaagent.jar","-Dotel.service.name=kotlin-zerocode","-jar","build/libs/ktor-sample-all.jar"]) -> process.executable.path: Str(/usr/lib/jvm/java-17-openjdk-amd64/bin/java) -> process.pid: Int(349907) -> process.runtime.description: Str(Debian OpenJDK 64-Bit Server VM 17.0.13+11-Debian-2deb12u1) -> process.runtime.name: Str(OpenJDK Runtime Environment) -> process.runtime.version: Str(17.0.13+11-Debian-2deb12u1) -> service.instance.id: Str(0da10240-547e-4a45-b28c-6133d3ffe1b5) -> service.name: Str(kotlin-zerocode) -> telemetry.distro.name: Str(opentelemetry-java-instrumentation) -> telemetry.distro.version: Str(2.12.0) -> telemetry.sdk.language: Str(java) -> telemetry.sdk.name: Str(opentelemetry) -> telemetry.sdk.version: Str(1.46.0) ScopeSpans #0 ScopeSpans SchemaURL: InstrumentationScope io.opentelemetry.netty-4.1 2.12.0-alpha Span #0 Trace ID : 287f6d7cab81f813910f909ac1ff0570 Parent ID : ID : aac3e33359775de7 Name : GET / Kind : Server Start time : 2025-01-19 08:50:03.077627907 +0000 UTC End time : 2025-01-19 08:50:03.126250246 +0000 UTC Status code : Unset Status message : Attributes: -> network.peer.address: Str(127.0.0.1) -> server.address: Str(localhost) -> client.address: Str(127.0.0.1) -> url.path: Str(/) -> server.port: Int(8080) -> http.request.method: Str(GET) -> thread.id: Int(35) -> http.response.status_code: Int(200) -> http.route: Str(/) -> user_agent.original: Str(curl/7.88.1) -> network.peer.port: Int(34344) -> network.protocol.version: Str(1.1) -> url.scheme: Str(http) -> thread.name: Str(eventLoopGroupProxy-3-1) {"kind": "exporter", "data_type": "traces", "name": "debug"} 2025-01-19T17:50:39.123+0900 info Traces {"kind": "exporter", "data_type": "traces", "name": "debug", "resource spans": 1, "spans": 1} 2025-01-19T17:50:39.123+0900 info ResourceSpans #0 Resource SchemaURL: https://opentelemetry.io/schemas/1.24.0 Resource attributes: -> host.arch: Str(amd64) -> host.name: Str(...) -> os.description: Str(Linux 6.1.0-30-amd64) -> os.type: Str(linux) -> process.command_args: Slice(["/usr/lib/jvm/java-17-openjdk-amd64/bin/java","-javaagent:./opentelemetry-javaagent.jar","-Dotel.service.name=kotlin-zerocode","-jar","build/libs/ktor-sample-all.jar"]) -> process.executable.path: Str(/usr/lib/jvm/java-17-openjdk-amd64/bin/java) -> process.pid: Int(349907) -> process.runtime.description: Str(Debian OpenJDK 64-Bit Server VM 17.0.13+11-Debian-2deb12u1) -> process.runtime.name: Str(OpenJDK Runtime Environment) -> process.runtime.version: Str(17.0.13+11-Debian-2deb12u1) -> service.instance.id: Str(0da10240-547e-4a45-b28c-6133d3ffe1b5) -> service.name: Str(kotlin-zerocode) -> telemetry.distro.name: Str(opentelemetry-java-instrumentation) -> telemetry.distro.version: Str(2.12.0) -> telemetry.sdk.language: Str(java) -> telemetry.sdk.name: Str(opentelemetry) -> telemetry.sdk.version: Str(1.46.0) ScopeSpans #0 ScopeSpans SchemaURL: InstrumentationScope io.opentelemetry.netty-4.1 2.12.0-alpha Span #0 Trace ID : 5b2a39ea9e9b83b2695d6f537901a5b8 Parent ID : ID : 1009964c6a9cba86 Name : GET /error Kind : Server Start time : 2025-01-19 08:50:38.102411829 +0000 UTC End time : 2025-01-19 08:50:38.112710928 +0000 UTC Status code : Error Status message : Attributes: -> network.peer.address: Str(127.0.0.1) -> server.address: Str(localhost) -> client.address: Str(127.0.0.1) -> url.path: Str(/error) -> error.type: Str(500) -> server.port: Int(8080) -> http.request.method: Str(GET) -> thread.id: Int(40) -> http.response.status_code: Int(500) -> http.route: Str(/error) -> user_agent.original: Str(curl/7.88.1) -> network.peer.port: Int(60186) -> network.protocol.version: Str(1.1) -> url.scheme: Str(http) -> thread.name: Str(eventLoopGroupProxy-3-2) {"kind": "exporter", "data_type": "traces", "name": "debug"} 2025-01-19T17:50:44.124+0900 info Traces {"kind": "exporter", "data_type": "traces", "name": "debug", "resource spans": 1, "spans": 1} 2025-01-19T17:50:44.124+0900 info ResourceSpans #0 Resource SchemaURL: https://opentelemetry.io/schemas/1.24.0 Resource attributes: -> host.arch: Str(amd64) -> host.name: Str(...) -> os.description: Str(Linux 6.1.0-30-amd64) -> os.type: Str(linux) -> process.command_args: Slice(["/usr/lib/jvm/java-17-openjdk-amd64/bin/java","-javaagent:./opentelemetry-javaagent.jar","-Dotel.service.name=kotlin-zerocode","-jar","build/libs/ktor-sample-all.jar"]) -> process.executable.path: Str(/usr/lib/jvm/java-17-openjdk-amd64/bin/java) -> process.pid: Int(349907) -> process.runtime.description: Str(Debian OpenJDK 64-Bit Server VM 17.0.13+11-Debian-2deb12u1) -> process.runtime.name: Str(OpenJDK Runtime Environment) -> process.runtime.version: Str(17.0.13+11-Debian-2deb12u1) -> service.instance.id: Str(0da10240-547e-4a45-b28c-6133d3ffe1b5) -> service.name: Str(kotlin-zerocode) -> telemetry.distro.name: Str(opentelemetry-java-instrumentation) -> telemetry.distro.version: Str(2.12.0) -> telemetry.sdk.language: Str(java) -> telemetry.sdk.name: Str(opentelemetry) -> telemetry.sdk.version: Str(1.46.0) ScopeSpans #0 ScopeSpans SchemaURL: InstrumentationScope io.opentelemetry.netty-4.1 2.12.0-alpha Span #0 Trace ID : b3d80bd3e7474a883e1213c4688219df Parent ID : ID : efb2d40bf565f9f9 Name : GET /500 Kind : Server Start time : 2025-01-19 08:50:40.462618021 +0000 UTC End time : 2025-01-19 08:50:40.464162334 +0000 UTC Status code : Error Status message : Attributes: -> network.peer.address: Str(127.0.0.1) -> server.address: Str(localhost) -> client.address: Str(127.0.0.1) -> url.path: Str(/500) -> error.type: Str(500) -> server.port: Int(8080) -> http.request.method: Str(GET) -> thread.id: Int(42) -> http.response.status_code: Int(500) -> http.route: Str(/500) -> user_agent.original: Str(curl/7.88.1) -> network.peer.port: Int(56010) -> network.protocol.version: Str(1.1) -> url.scheme: Str(http) -> thread.name: Str(eventLoopGroupProxy-3-3) {"kind": "exporter", "data_type": "traces", "name": "debug"}
.NETとだいたい同じ雰囲気だね。
Collectorでメトリックも受け付けを有効にしてみたところ、HTTP request duration、送ったスパンの数、JVMリソース状況などがメトリックとして送出されてきていた(Goや.NETもメトリックを送ってきていたんだけどそういえばちゃんと見ていなかったな)。
様子を見たいときにはOpenTelemetry Collectorの設定を以下のように変えればよい。
service: pipelines: metrics: receivers: [otlp] exporters: [debug] logs: receivers: [otlp] exporters: [debug] traces: receivers: [otlp] exporters: [debug]
Springもそのうち試そうと思うが、まずはほかの言語をひととおり見てからにする。
いまどきJavaで書くには何らかのフレームワークを使っているだろうから、Java Agentによるzero-code計装は.NET同様に手軽だし便利そうだなと感じた。
残りはPHP、Python、JavaScriptか〜
ふらっと見てたらこんなissueがあった。