OpenTelemetry Tracing のデモには、2 つの Spring Boot コンポーネントが含まれています。1 つは Java エージェントを使用しており、最近 v1.x から v2.x にアップグレードしたときに動作が異なることに気付きました。もう 1 つは、GraalVM ネイティブにコンパイルするため、Micrometer Tracing を使用していますが、Java エージェントを処理できません。
この記事では、Java エージェント v1、Java エージェント v2、Micrometer Tracing の 3 つのアプローチを比較します。
同じベース アプリケーション、つまり Kotlin でコーディングされたシンプルな Spring Boot アプリケーションを使用します。単一のエンドポイントを提供します。
entry()
という名前ですintermediate()
という別の関数を呼び出します。RestTemplate
の代わりとなるWebClient
インスタンスを使用して、上記のエンドポイントを呼び出します。entry()
関数がそれを見つけた場合、それ以上先に進みません。
これは次のコードに変換されます。
@SpringBootApplication class Agent1xApplication @RestController class MicrometerController { private val logger = LoggerFactory.getLogger(MicrometerController::class.java) @GetMapping("/{message}") fun entry(@PathVariable message: String, @RequestHeader("X-done") done: String?) { logger.info("entry: $message") if (done == null) intermediate() } fun intermediate() { logger.info("intermediate") RestClient.builder() .baseUrl("http://localhost:8080/done") .build() .get() .header("X-done", "true") .retrieve() .toBodilessEntity() } }
すべてのセットアップについて、OpenTelemetry が有効になっているプライマリ ステージと、追加の内部スパンを作成するためのカスタマイズ ステージの 2 つのステージを確認します。
Micrometer Tracing は、「ベンダー中立のアプリケーション可観測性ファサード」であるMicrometerから派生したものです。
Micrometer Tracing は、最も人気のあるトレーサー ライブラリのシンプルなファサードを提供し、ベンダー ロックインなしで JVM ベースのアプリケーション コードをインストルメント化できるようにします。トレースの収集アクティビティにオーバーヘッドをほとんど追加せず、トレース作業の移植性を最大限に高めるように設計されています。
Micrometer Tracing を開始するには、いくつかの依存関係を追加する必要があります。
org.springframework.boot:spring-boot-starter-actuator
io.micrometer:micrometer-tracing
io.micrometer:micrometer-tracing-bridge-otel
です。io.opentelemetry:opentelemetry-exporter-otlp
バージョンは Spring Boot の親ですでに定義されているため、BOM は必要ありません。
ただし、トレースの送信先とコンポーネントの名前という 2 つのランタイム構成パラメータが必要です。これらは、 MANAGEMENT_OTLP_TRACING_ENDPOINT
変数とSPRING_APPLICATION_NAME
変数によって制御されます。
services: jaeger: image: jaegertracing/all-in-one:1.55 environment: - COLLECTOR_OTLP_ENABLED=true #1 ports: - "16686:16686" micrometer-tracing: build: dockerfile: Dockerfile-micrometer environment: MANAGEMENT_OTLP_TRACING_ENDPOINT: http://jaeger:4318/v1/traces #2 SPRING_APPLICATION_NAME: micrometer-tracing #3
結果は次のとおりです。
カスタマイズを行わない場合、Micrometer は HTTP リクエストの受信と送信時にスパンを作成します。
フレームワークは、送信のためにRestClient
に魔法を注入する必要があります。そのためには、前者が後者をインスタンス化できるようにする必要があります。
@SpringBootApplication class MicrometerTracingApplication { @Bean fun restClient(builder: RestClient.Builder) = builder.baseUrl("http://localhost:8080/done").build() }
手動スパンは、OpenTelemetry API 自体を使用する方法など、いくつかの方法で作成できます。ただし、セットアップには多くの定型コードが必要です。最も簡単な方法は、Micrometer のObservation API を使用することです。その主な利点は、メトリックとトレースの両方を管理する単一の API を使用できることです。
更新されたコードは次のとおりです。
class MicrometerController( private val restClient: RestClient, private val registry: ObservationRegistry ) { @GetMapping("/{message}") fun entry(@PathVariable message: String, @RequestHeader("X-done") done: String?) { logger.info("entry: $message") val observation = Observation.start("entry", registry) if (done == null) intermediate(observation) observation.stop() } fun intermediate(parent: Observation) { logger.info("intermediate") val observation = Observation.createNotStarted("intermediate", registry) .parentObservation(parent) .start() restClient.get() .header("X-done", "true") .retrieve() .toBodilessEntity() observation.stop() } }
追加された観察呼び出しは、生成されたトレースに反映されます。
Micrometer Tracing の代替として、汎用OpenTelemetry Java Agentがあります。その主な利点は、コードにも開発者にも影響を与えないことです。エージェントは、純粋にランタイム スコープの問題です。
java -javaagent:opentelemetry-javaagent.jar agent-one-1.0-SNAPSHOT.jar
エージェントは、環境変数を使用した OpenTelemetry の構成に従います。
services: agent-1x: build: dockerfile: Dockerfile-agent1 environment: OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4317 #1 OTEL_RESOURCE_ATTRIBUTES: service.name=agent-1x #2 OTEL_METRICS_EXPORTER: none #3 OTEL_LOGS_EXPORTER: none #4 ports: - "8081:8080"
/v1/traces
を追加します。
これ以上の設定を行わない場合、次のトレースが得られます。
エージェントは、受信および送信されたリクエストと、Spring 関連のアノテーションでマークされた関数を自動的に追跡します。トレースは、コール スタックに従って、互いに正しくネストされます。追加の関数をトレースするには、コードベースに依存関係io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations
を追加する必要があります。これで、以前はトレースされていなかった関数に@WithSpan
アノテーションを付けることができます。
value()
部分はトレースのラベルを制御し、 kind
span.kind
属性として変換されます。値が空の文字列に設定されている場合 (デフォルト)、関数の名前が出力されます。私の目的には、デフォルト値で十分です。
@WithSpan fun intermediate() { logger.info("intermediate") RestClient.builder() .baseUrl("http://localhost:8080/done") .build() .get() .header("X-done", "true") .retrieve() .toBodilessEntity() }
期待どおりの新しいintermediate()
トレースが生成されます。
OpenTelemetry は今年 1 月にエージェントの新しいメジャー バージョンをリリースしました。私はデモをそれに合わせて更新しました。これで、トレースはアプリがリクエストを受信および送信するときにのみ作成されるようになりました。
以前のバージョンと同様に、 @WithSpan
アノテーションを使用してトレースを追加できます。唯一の違いは、 entry()
関数にもアノテーションを付けなければならないことです。デフォルトではトレースされません。
Spring が成功した理由は 2 つあります。複雑なソリューション (EJB 2など) を簡素化し、競合ライブラリに対する抽象化レイヤーを提供したことです。Micrometer Tracing は Zipkin と Jaeger に対する抽象化レイヤーとして始まり、完全に理にかなったものでした。この議論は、OpenTelemetry がプログラミング言語やトレース コレクターのほとんどのライブラリでサポートされているため、意味をなさなくなりました。Observation API は、Metrics と Traces に対して単一の API を使用するため、依然として Micrometer Tracing の大きな利点です。
Java エージェント側では、OpenTelemetry 構成はすべての技術スタックとライブラリ (環境変数) で同様です。新しいエージェントは Spring に対応していないため、v1 から v2 にアップグレードしたときに少しがっかりしました。Spring アノテーション付き関数はデフォルトではトレースされません。
結局のところ、これは賢明な決断です。表示したくないスパンを削除するよりも、必要なスパンを明示的に指定したほうがはるかに良いのです。
ご協力とレビューをいただいたJonatan Ivanov 氏に感謝します。
この投稿の完全なソースコードは GitHub で見つかります:
さらに進むには:
2024年8月3日にA Java Geekで最初に公開されました