これは何
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.OnMethodExit
Annotationが付与された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.http
Moduleに対するinstrumentationを見ながら実装を追っていきます.
java.net.http
Moduleの使用方法
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());
サンプルコードよりHttpClient
Classのsend
Methodを実行することでHTTPリクエストを送信しています.このことからHttpClient
Classのsend
Methodの前後にSpanの生成処理を追加することでinstrumentationが行えると考えられます.(sendAsync
Methodについては一旦割愛します)
Java agent instrumentationの実装について
ここから実際のinstrumentationの実装を見ていきます.
Java Agent instrumentation実装時の作法については以下のドキュメントにて解説されているので,こちらに沿って確認していきます.
InstrumentationModule
ClassとTypeInstrumentation
Interfaceについて
Java Agent InstrumentationはTypeInstrumentation
InterfaceとInstrumentationModule
Class
で構成されています.
TypeInstrumentation
Interface
TypeInstrumentation
Interfaceはライブラリ内の個々のClassに対するInstrumentation処理を実装しているClassが実装するInterfaceです.
typeMatcher
Methodで対象とするClassを指定し,transform
MethodでClassに対して行うInstrumentation処理を実装しています.
実際にjava.net.http.HttpClient
Classへの処理を見てみると,bytebuddy
の ElementMatcher
を用いてClassを指定しsend
Methodに対してSendAdvice
Classとの紐づけを行っています.
この処理により SenAdvice
ClassのmethodEnter
Method, methodEnter
Methodがそれぞれ send
Methodの実行前後に実行されます.
これらのMethod内ではInstrumenter API(OpentelemetryのAPIのラッパー)を実行しており,Spanの開始~終了やContextの生成を行っています.
InstrumentationModule
Class
InstrumentationModule
Classは前述の TypeInstrumentation
Interfaceを実装したClassをライブラリごとに集約しJava Agent実行時に読み込まれるようにするためのClassです.
@AutoService
AnotationでServiceLoader API経由でJava Agentから読み込まれ,typeInstrumentations
Methodで TypeInstrumentation
Interfaceを実装したClassのインスタンスを返すことでJava AgentにInstrumentationを実行させます.
実際にjava.net.http.HttpClient
Classへの処理を見てみると,java.net.http.HttpClient
ClassへのInstrumentationを実装したClassのインスタンスが typeInstrumentations
Methodで返されていることがわかります.(HttpHeadersInstrumentation
Classは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について解説されているドキュメントです.