OpenTelemetryのTracingについてまとめる JavaにおけるAuto Instrumentation編
これは何
OpenTelemetryのTracingについて理解するため,GitHub - open-telemetry/opentelemetry-java-instrumentation at v2.6.0において以下について調査した結果をまとめる記事です.
- Instrumentaionの種類について
- JavaにおけるAuto Instrumentationの実装方法
まとめ
javaコマンドの-javaagentオプションでJava Agentを渡し,アプリケーションの実行前にInstrumentationを行っている- Java Agent内ではByte Buddyの
ElementMatchersとAdviceを用いてInstrumentationを行っているElementMatchersを用いてInstrumentationを行うClass, Methodを指定,実行前後に追加する処理を実装したClassと紐づけを行う- 紐づけられるClassでは
@Advice.OnMethodEnter,@Advice.OnMethodExitAnnotationが付与されたMethodsが実装されており,Spanの生成,Contextの新規生成を行う
Java Agentからの呼び出しは以下のようになっています.

Instrumentaionの種類について
JavaにおけるライブラリごとのInstrumentationには以下の2種類が公開されています.
Library instrumentation
対象のライブラリ固有の仕組みを用いたInstrumentationの実装です.
例としてservletの場合Filter機能と呼ばれるリクエストの前後で任意の処理を実行できる機能が提供されています.
このFilter内でSpanの生成などを行うことでInstrumentationを実現しています.
この方式のInstrumentationを行う場合はユーザーはlibrary instrumentationで提供されるClassの初期化を行うため,アプリケーションのコードを変更する必要があります.
Java agent instrumentation
Java agent instrumentationではユーザーによるアプリケーションのコードの変更を行わず,自動的にinstrumentationを行えます.
README.md記載の通りアプリケーション実行時にJava Agentを指定するのみで自動的にinstrumentationが行われます.
java -javaagent:path/to/opentelemetry-javaagent.jar \
-jar myapp.jar
JavaにおけるAuto Instrumentationの実装方法
上記のリポジトリでは instrumentation 配下にライブラリごとのinstrumentationの実装が配置されています.
ここからはJava標準ライブラリの一つであるjava.net.httpModuleに対するinstrumentationを見ながら実装を追っていきます.
java.net.httpModuleの使用方法
instrumentationの実装を追う前に対象のライブラリがどのように使われているかを知っておく必要があるため,サンプルコードを確認します.
HttpClient client = HttpClient.newBuilder()
.version(Version.HTTP_1_1)
.followRedirects(Redirect.NORMAL)
.connectTimeout(Duration.ofSeconds(20))
.proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 80)))
.authenticator(Authenticator.getDefault())
.build();
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
サンプルコードよりHttpClientClassのsendMethodを実行することでHTTPリクエストを送信しています.このことからHttpClientClassのsendMethodの前後にSpanの生成処理を追加することでinstrumentationが行えると考えられます.(sendAsyncMethodについては一旦割愛します)
Java agent instrumentationの実装について
ここから実際のinstrumentationの実装を見ていきます.
Java Agent instrumentation実装時の作法については以下のドキュメントにて解説されているので,こちらに沿って確認していきます.
InstrumentationModule ClassとTypeInstrumentationInterfaceについて
Java Agent InstrumentationはTypeInstrumentationInterfaceとInstrumentationModule Class
で構成されています.
TypeInstrumentationInterface
TypeInstrumentationInterfaceはライブラリ内の個々のClassに対するInstrumentation処理を実装しているClassが実装するInterfaceです.
typeMatcherMethodで対象とするClassを指定し,transformMethodでClassに対して行うInstrumentation処理を実装しています.
実際にjava.net.http.HttpClientClassへの処理を見てみると,bytebuddyの ElementMatcherを用いてClassを指定しsendMethodに対してSendAdviceClassとの紐づけを行っています.
この処理により SenAdviceClassのmethodEnterMethod, methodEnterMethodがそれぞれ sendMethodの実行前後に実行されます.
これらのMethod内ではInstrumenter API(OpentelemetryのAPIのラッパー)を実行しており,Spanの開始~終了やContextの生成を行っています.
InstrumentationModule Class
InstrumentationModule Classは前述の TypeInstrumentationInterfaceを実装したClassをライブラリごとに集約しJava Agent実行時に読み込まれるようにするためのClassです.
@AutoServiceAnotationでServiceLoader API経由でJava Agentから読み込まれ,typeInstrumentationsMethodで TypeInstrumentationInterfaceを実装したClassのインスタンスを返すことでJava AgentにInstrumentationを実行させます.
実際にjava.net.http.HttpClientClassへの処理を見てみると,java.net.http.HttpClientClassへのInstrumentationを実装したClassのインスタンスが typeInstrumentationsMethodで返されていることがわかります.(HttpHeadersInstrumentationClassはTrace Context伝搬のためのInstrumentationを実装したClassです)
感想
- 以下について掘り下げないと自力でInstrumentationを実装するのは厳しそう😭
- Byte Buddy
- AdviceやElement Matcherなど
- Service(Javaにおける)
- Byte Buddy
参考資料
instrumentationの作法について書かれたドキュメント
instrumentation実装時の作法について記載されているため,まずはここを読むのがよさそうです.
Java agent instrumentation実装時の作法について書かれたドキュメント
Java agentから利用されるClassを実装する際の手順について書かれています.
Instrumenter APIについて解説されたドキュメント
instrumentation実装時に利用される Instrumenter APIについて解説されているドキュメントです.