OpenTelemetry Tracing の使用
このガイドでは、Quarkus アプリケーションで OpenTelemetry (OTel) を利用して、 インタラクティブな Web アプリケーションに分散トレースを提供する方法について説明します。
このドキュメントは、このコンポーネントとその他のオブザーバビリティ関連コンポーネントを特集した Quarkus のオブザーバビリティリファレンス ガイド の一部です。
|
要件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOMEが適切に設定されていること -
Apache Maven 3.9.15
-
Docker と Docker Compose、または Podman 、および Docker Compose
-
使用したい場合は、 Quarkus CLI
-
ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること
ソリューション
次の章で紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。 ただし、すぐに完成した例に飛んでも構いません。
Clone the Git repository: git clone https://github.com/quarkusio/quarkus-quickstarts.git, or download an archive.
解決策は opentelemetry-quickstart ディレクトリー にあります。
Maven プロジェクトの作成
まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します。
Windowsユーザーの場合:
-
cmdを使用する場合、(バックスラッシュ
\を使用せず、すべてを同じ行に書かないでください)。 -
Powershellを使用する場合は、
-Dパラメータを二重引用符で囲んでください。例:"-DprojectArtifactId=opentelemetry-quickstart"
このコマンドはMavenプロジェクトを生成し、 quarkus-opentelemetry エクステンションをインポートします。
このエクステンションには、デフォルトのOpenTelemetryサポート
と、 OTLPのgRPC spanエクスポーターが含まれています。
Quarkus プロジェクトがすでに設定されている場合、プロジェクトのベースディレクトリーで次のコマンドを実行することで、
quarkus-opentelemetry エクステンションをプロジェクトに追加できます:
quarkus extension add opentelemetry
./mvnw quarkus:add-extension -Dextensions='opentelemetry'
./gradlew addExtension --extensions='opentelemetry'
これにより、ビルドファイルに次の内容が追加されます。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-opentelemetry</artifactId>
</dependency>
implementation("io.quarkus:quarkus-opentelemetry")
Jakarta REST リソースの調査
src/main/java/org/acme/opentelemetry/TracedResource.java のファイルを開くと、以下の内容が表示されます。
package org.acme.opentelemetry;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.jboss.logging.Logger;
@Path("/hello")
public class TracedResource {
private static final Logger LOG = Logger.getLogger(TracedResource.class);
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
LOG.info("hello");
return "hello";
}
}
このアプリケーションには、トレースのためのコードが含まれていないことに注意してください。デフォルトでは、 このエンドポイントに送信されたリクエストは、コードの変更を必要とせずにトレースされます。
構成の作成
デフォルトでは、エクスポータはgRPCプロトコルとエンドポイント http://localhost:4317 を使用して、バッチでデータを送信します。
デフォルトのプロパティ値を変更する必要がある場合、 src/main/resources/application.properties ファイルを使用して、アプリケーション内でデフォルトの OTLP gRPC Exporter を設定する方法の例を以下に示します:
quarkus.application.name=myservice (1)
quarkus.otel.exporter.otlp.endpoint=http://localhost:4317 (2)
quarkus.otel.exporter.otlp.headers=authorization=Bearer my_secret (3)
quarkus.log.console.format=%d{HH:mm:ss} %-5p traceId=%X{traceId}, parentId=%X{parentId}, spanId=%X{spanId}, sampled=%X{sampled} [%c{2.}] (%t) %s%e%n (4)
# Alternative to the console log
quarkus.http.access-log.pattern="...traceId=%{X,traceId} spanId=%{X,spanId}" (5)
| 1 | アプリケーションから作成されたすべてのテレメトリには、 myservice アプリケーションによって作成されたことを示す OpenTelemetry Resource 属性が含まれます。
設定されていない場合、デフォルトでアーティファクト ID が設定されます。 |
| 2 | テレメトリを送信する gRPC エンドポイント。設定されていない場合、デフォルトは http://localhost:4317 . |
| 3 | 認証によく使われるオプションのgRPCヘッダー |
| 4 | ログメッセージにトレース情報を追加する。 |
| 5 | また、アクセスログにのみトレース情報を記載することもできます。この場合、コンソールログ形式の情報を省略する必要があります。 |
接続関連のプロパティには、シグナルに依存しない設定を提供しています。つまり、設定時にトレースとメトリクスの両方に同じプロパティを使用できます:
quarkus.otel.exporter.otlp.endpoint=http://localhost:4317
シグナルごとに異なる設定が必要な場合は、特定のプロパティを使用できます:
quarkus.otel.exporter.otlp.traces.endpoint=http://trace-uri:4317 (1)
quarkus.otel.exporter.otlp.metrics.endpoint=http://metrics-uri:4317 (2)
quarkus.otel.exporter.otlp.logs.endpoint=http://logs-uri:4317 (3)
| 1 | トレースエクスポーターのエンドポイント。 |
| 2 | メトリクスエクスポーターのエンドポイント。 |
| 3 | ログエクスポーターのエンドポイント。 |
スパンとログを完了と同時に直接エクスポートする必要がある場合 (サーバーレス環境やアプリケーションなど)、このプロパティーを true に設定できます。これにより、データのデフォルトのバッチ処理が置き換えられます。
quarkus.otel.simple=true
See data
Before starting the app, please set up the system to visualize the OpenTelemetry data. We have several options:
-
Start an all-in-one Grafana OTel LGTM Dev Service for traces, logs and metrics
-
Jaeger system just for traces
-
Logging エクスポーター
Grafana OTel LGTM オプション
A Dev Service that will receive your app’s telemetry. Only the dependency is needed.
-
Grafana-OTel-LGTM のスタートガイド を参照してください。
This features a Quarkus Dev Service including a Grafana for visualizing data, Loki to store logs, Tempo to store traces and Prometheus to store metrics. Also provides an OTel collector to receive the data.
Jaeger v2 to see traces option
Jaeger V2 is a tool to visualize spans, and it’s based on OpenTelemetry collector. There is no need to install a separate collector. More details are available in this blog post.
Start the OpenTelemetry Collector and Jaeger system via the following Docker command:
docker run -it \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
jaegertracing/jaeger:latest
以下の場合:
| Port | Purpose |
|---|---|
16686 |
Jaeger UI |
4317 |
OpenTelemetry collector. OTLP protobuf gRPC receiver. |
4318 |
OpenTelemetry collector. OTLP protobuf HTTP receiver. |
Other ports are available in the Jaeger documentation.
Logging エクスポーター
You can output all traces to the console by setting the exporter to logging in the application.properties file:
quarkus.otel.traces.exporter=logging
|
Usually there is no need to set the |
This dependency must be added to the project:
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-logging</artifactId>
</dependency>
アプリケーションの実行
アプリケーションを起動します。
これでアプリケーションを実行する準備が整いました。トレーサーの設定に application.properties を使用している場合:
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
または、JVM引数でOTLP gRPCエンドポイントを設定する場合:
quarkus dev -Djvm.args="-Dquarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4317"
./mvnw quarkus:dev -Djvm.args="-Dquarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4317"
./gradlew --console=plain quarkusDev -Djvm.args="-Dquarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4317"
OpenTelemetry Collector、Jaegerシステム、アプリケーションが動作している状態で、提供されているエンドポイントにリクエストを出すことができます。
$ curl http://localhost:8080/hello
hello
最初のリクエストが送信された時点で、ログにトレース情報が表示されるようになります:
10:49:02 INFO traceId=, parentId=, spanId=, sampled= [io.quarkus] (main) Installed features: [cdi, opentelemetry, resteasy-client, resteasy, smallrye-context-propagation, vertx]
10:49:03 INFO traceId=17ceb8429b9f25b0b879fa1503259456, parentId=3125c8bee75b7ad6, spanId=58ce77c86dd23457, sampled=true [or.ac.op.TracedResource] (executor-thread-1) hello
10:49:03 INFO traceId=ad23acd6d9a4ed3d1de07866a52fa2df, parentId=, spanId=df13f5b45cf4d1e2, sampled=true [or.ac.op.TracedResource] (executor-thread-0) hello
その後、 Jaeger UI にアクセスしてトレース情報を確認します。
CTRL+C を押すか、 q と入力して、アプリケーションを停止してください。
JDBC
このエクステンションにバンドルされている JDBC 計装 は、アプリケーションによって行われる各 JDBC クエリーに対してスパンを追加します。
専用の JDBC データソースラッパーを使用するため、以下の例のように quarkus.datasource.jdbc.telemetry プロパティーを使用してデータソースのテレメトリーを有効にする必要があります。
# enable tracing
quarkus.datasource.jdbc.telemetry=true
# configure datasource
quarkus.datasource.db-kind=postgresql
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/mydatabase
For more details check the Datasource tracing documentation.
追加設定
一部のユースケースでは、OpenTelemetry のカスタム設定が必要になります。 これらのセクションでは、適切に設定するために必要なものについて概説します。
ID ジェネレーター
OpenTelemetry エクステンションでは、トレースおよびスパンの識別子を作成する際に、デフォルトでランダムな ID ジェネレーター を使用します。
ベンダー固有のプロトコルの中には、カスタム ID ジェネレーターを必要とするものがありますが、
プロデューサーを作成することで、デフォルトの ID ジェネレーターを上書きすることができます。OpenTelemetry エクステンションは、 IdGenerator CDI Bean を検出し、トレーサープロデューサーを設定する際にそれを使用します。
@Singleton
public class CustomConfiguration {
/** Creates a custom IdGenerator for OpenTelemetry */
@Produces
@Singleton
public IdGenerator idGenerator() {
return AwsXrayIdGenerator.getInstance();
}
}
プロパゲーター
OpenTelemetry は、 プロパゲーター を介して分野横断的な関心事を伝播し、状態を保存するための基になる Context を共有し、分散トランザクションの存続期間全体にわたってデータにアクセスします。
デフォルトでは、OpenTelemetryエクステンションは、W3C Trace Contextと W3C Baggage
プロパゲーターを有効にしていますが、OpenTelemetry 設定リファレンス で説明されている propagators 設定を行うことで、サポートされているOpenTelemetryプロパゲーターのいずれかを選択できます。
追加プロパゲーター
-
b3,b3multi,jaeger,ottraceのプロパゲーターは、プロジェクトに trace-propagators エクステンションを依存関係として追加する必要があります。
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-extension-trace-propagators</artifactId>
</dependency>
implementation("io.opentelemetry:opentelemetry-extension-trace-propagators")
-
xrayプロパゲーターは、お客様のプロジェクトに aws エクステンションを依存関係として追加することを必要とします。
<dependency>
<groupId>io.opentelemetry.contrib</groupId>
<artifactId>opentelemetry-aws-xray-propagator</artifactId>
</dependency>
implementation("io.opentelemetry.contrib:opentelemetry-aws-xray-propagator")
プロパゲーターをカスタマイズする
伝播ヘッダーをカスタマイズするには、 TextMapPropagatorCustomizer インターフェイスを実装します。たとえば、これを使用して、OpenTelemetry トレースヘッダーの伝播を制限し、機密性の高いデータがサードパーティーのシステムに送信されないようにすることができます。
/**
* /**
* Meant to be implemented by a CDI bean that provides arbitrary customization for the TextMapPropagator
* that are to be registered with OpenTelemetry
*/
public interface TextMapPropagatorCustomizer {
TextMapPropagator customize(Context context);
interface Context {
TextMapPropagator propagator();
ConfigProperties otelConfigProperties();
}
}
リソース
メインの OpenTelemetry ガイドリソース セクションを参照してください。
エンドユーザー属性
有効にすると、Quarkus は OpenTelemetry エンドユーザー属性を Span 属性として追加します。 この機能を有効にする前に、Quarkus Security エクステンションが存在し、設定されていることを確認してください。 Quarkus Security の詳細は、Quarkus Security の概要 を参照してください。
属性は、ベストエフォート方式で認証がすでに行われている場合にのみ追加されます。
End User 属性が Span 属性として追加されるかどうかは、Quarkus アプリケーションの認証および認可の設定によって異なります。
認証前にカスタム Span を作成した場合、Quarkus はそれらに End User 属性を追加できません。
Quarkus は、認証が完了した後にのみ、現在の Span に属性を追加できます。
カスタム Span に関するもう 1 つの重要な考慮事項は、Quarkus の SecurityIdentity を伝播するために使用されるアクティブな CDI リクエストコンテキストです。
原則として、カスタム Span が作成される前に CDI リクエストコンテキストがアクティブ化されている場合、Quarkus は End User 属性を追加できます。
quarkus.otel.traces.eusp.enabled=true (1)
quarkus.http.auth.proactive=true (2)
| 1 | End User 属性機能を有効にして、 SecurityIdentity プリンシパルとロールが Span 属性として追加されるようにします。
End User 属性は個人を特定できる情報であるため、この機能を有効にする前にエクスポートすることを確認してください。 |
| 2 | 必要に応じて、プロアクティブ認証を有効にします。 プロアクティブ認証を有効にすると、認証がより早く行われるため、最良の結果が得られます。 Quarkus アプリケーションでプロアクティブ認証を有効にする必要があるかどうかを判断する良い方法は、Quarkus の プロアクティブ認証 ガイドを読むことです。 |
| この機能は、カスタム Jakarta REST SecurityContexts が使用されている場合にはサポートされません。 |
サンプラー
サンプラー は、トレースを破棄するか転送するかを決定し、コレクターに送信される収集されたトレースの数を制限することで、ノイズを効果的に管理し、オーバーヘッドを削減します。
Quarkus には 組み込みサンプラー が装備されており、カスタムサンプラーを作成するオプションもあります。
組み込みサンプラーを使用するには、OpenTelemetry 設定リファレンス の説明にあるように、必要なサンプラーパラメーターを設定することで、設定できます。たとえば、トレースの 50% を保持するようにサンプラーを設定できます。
# build time property only:
quarkus.otel.traces.sampler=traceidratio
# Runtime property:
quarkus.otel.traces.sampler.arg=0.5
|
サンプラーの興味深いユースケースは、次の例のように、実行時にトレーシングのエクスポートをアクティブ化または非アクティブ化することです。
|
カスタムサンプラーを使用する必要がある場合、現在2種類の方法があります:
サンプラー CDI プロデューサー
サンプラー CDI プロデューサーを作成できます。Quarkus OpenTelemetry エクステンションは Sampler CDI Bean を検出し、Tracer を設定するときにそれを使用します。
@Singleton
public class CustomConfiguration {
/** Creates a custom sampler for OpenTelemetry */
@Produces
@Singleton
public Sampler sampler() {
return JaegerRemoteSampler.builder()
.setServiceName("my-service")
.build();
}
}
OTelサンプラーSPI
これにより、OTel 自動設定で使用可能な SPI フックが使用されます。 シンプルな Sampler クラスを作成できます。
public class CustomSPISampler implements Sampler {
@Override
public SamplingResult shouldSample(Context context,
String s,
String s1,
SpanKind spanKind,
Attributes attributes,
List<LinkData> list) {
// Do some sampling here
return Sampler.alwaysOn().shouldSample(context, s, s1, spanKind, attributes, list);
}
@Override
public String getDescription() {
return "custom-spi-sampler-description";
}
}
次に Sampler Provider を作成できます。
public class CustomSPISamplerProvider implements ConfigurableSamplerProvider {
@Override
public Sampler createSampler(ConfigProperties configProperties) {
return new CustomSPISampler();
}
@Override
public String getName() {
return "custom-spi-sampler";
}
}
resources/META-INF/services に io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider という名前の SPI ローダーテキストファイルを作成し、その中に CustomSPISamplerProvider クラスの完全修飾名を記述します。
次に、設定を有効にします。
quarkus.otel.traces.sampler=custom-spi-sampler
ご覧のとおり、CDI の方が操作がはるかに簡単です。
追加の計器
Quarkus エクステンションの中には、トレースが後続の実行に伝搬されることを保証するために追加のコードを必要とするものがあります。 これらのセクションでは、プロセスの境界を越えてトレースを伝搬するために必要なことを説明します。
このセクションで説明されている計装は、Quarkus でテストされており、標準モードとネイティブモードの両方で動作します。
CDI
CDI 対応の任意の Bean のメソッドに io.opentelemetry.instrumentation.annotations.WithSpan
アノテーションを付与すると、新しい Span が作成され、現在の Trace コンテキストとの必要な関係が確立されます。
CDI 対応の任意の Bean のメソッドに io.opentelemetry.instrumentation.annotations.AddingSpanAttributes アノテーションを付与すると、新しい Span は作成されませんが、アノテーションが付けられたメソッドのパラメーターが現在の Span の属性として追加されます。
メソッドに誤って @AddingSpanAttributes アノテーションと @WithSpan アノテーションが付けられている場合は、 @WithSpan アノテーションが優先されます。
メソッドのパラメーターに io.opentelemetry.instrumentation.annotations.SpanAttribute アノテーションを付与することで、
どのメソッドパラメーターが Span の一部となるべきかを示すことができます。また、パラメーター名はカスタマイズすることも可能です。
以下に例を示します。
@ApplicationScoped
class SpanBean {
@WithSpan
void span() {
}
@WithSpan("name")
void spanName() {
}
@WithSpan(kind = SERVER)
void spanKind() {
}
@WithSpan
void spanArgs(@SpanAttribute(value = "arg") String arg) {
}
@AddingSpanAttributes
void addArgumentToExistingSpan(@SpanAttribute(value = "arg") String arg) {
}
}
使用可能なOpenTelemetry CDI依存関係注入
MicroProfile Telemetry Tracing の仕様に基づき、Quarkus は以下のクラスの CDI 依存関係注入 をサポートしています。
-
io.opentelemetry.api.OpenTelemetry -
io.opentelemetry.api.trace.Tracer -
io.opentelemetry.api.trace.Span -
io.opentelemetry.api.baggage.Baggage
これらのクラスは、CDIが有効なBeanに注入することができます。例えば、 Tracer は、カスタムスパンを開始するために特に有用です。
@Inject
Tracer tracer;
...
public void tracedWork() {
Span span = tracer.spanBuilder("My custom span")
.setAttribute("attr", "attr.value")
.setParent(Context.current().with(Span.current()))
.setSpanKind(SpanKind.INTERNAL)
.startSpan();
// traced work
span.end();
}
Mutiny
リアクティブな型を返すメソッドには、 @WithSpan と @AddingSpanAttributes のアノテーションを付けて、新しいスパンを作成したり、現在のスパンに属性を追加したりすることもできます。
mutiny パイプライン内でスパンを手動で作成する必要がある場合は、 io.quarkus.opentelemetry.runtime.tracing.mutiny.MutinyTracingHelper の wrapWithSpan メソッドを使用してください。
例えば、次のようなパイプラインがあるとします:
Uni<String> uni = Uni.createFrom().item("hello")
//start trace here
.onItem().transform(item -> item + " world")
.onItem().transform(item -> item + "!")
//end trace here
.subscribe().with(
item -> System.out.println("Received: " + item),
failure -> System.out.println("Failed with " + failure)
);
このようにラップして下さい:
import static io.quarkus.opentelemetry.runtime.tracing.mutiny.MutinyTracingHelper.wrapWithSpan;
...
@Inject
Tracer tracer;
...
Context context = Context.current();
Uni<String> uni = Uni.createFrom().item("hello")
.transformToUni(m -> wrapWithSpan(tracer, Optional.of(context), "my-span-name",
Uni.createFrom().item(m)
.onItem().transform(item -> item + " world")
.onItem().transform(item -> item + "!")
))
.subscribe().with(
item -> System.out.println("Received: " + item),
failure -> System.out.println("Failed with " + failure)
);
マルチパイプラインの場合も同様です:
Multi.createFrom().items("Alice", "Bob", "Charlie")
.transformToMultiAndConcatenate(m -> TracingHelper.withTrace("my-span-name",
Multi.createFrom().item(m)
.onItem().transform(name -> "Hello " + name)
))
.subscribe().with(
item -> System.out.println("Received: " + item),
failure -> System.out.println("Failed with " + failure)
);
アイテムの順番を気にしないのであれば、 transformToMultiAndConcatenate の代わりに transformToMultiAndMerge を使うこともできます。
Quarkus Messaging - Kafka
Kafka の Quarkus Messaging エクステンションを使用する場合、 以下のようにして、スパンを Kafka レコードに伝播できます。
TracingMetadata tm = TracingMetadata.withPrevious(Context.current());
Message out = Message.of(...).withMetadata(tm);
上記は、生成されている Message に追加できる TracingMetadata オブジェクトを作成し、
OpenTelemetry Context を取得して、伝播のために現在のスパンを抽出しています。
Quarkus Security イベント
Quarkus は、セキュリティーイベント を OpenTelemetry Span イベントとしてエクスポートすることをサポートしています。
quarkus.otel.security-events.enabled=true (1)
| 1 | Quarkus Security イベントを OpenTelemetry Span イベントとしてエクスポートします。 |
手動計装
ユーティリティーまたは自動計装が利用できない場合、手動計装を使用してスパンを生成し、OpenTelemetry コンテキストと Baggage を伝播することができます。
|
より多くのメンテナンス作業が必要になるため、他に選択肢がない場合にのみ手動計装を使用してください。 |
手動スパン
以下の例では、カスタム属性を持つ手動スパンを実装するメソッドを持つクラスがあります。そこで計測している唯一のビジネスロジックは、 return "Hello from Quarkus REST"; ステートメントです。
@ApplicationScoped
public static class HelloSpan {
private final Tracer tracer;
// Instead of using @Inject on the tracer attribute
public HelloSpan(Tracer tracer) {
// The same as openTelemetry.getTracer("io.quarkus.opentelemetry");
this.tracer = tracer;
}
public String helloManualSpan() {
// Create a new span
Span span = tracer.spanBuilder("HelloBean.helloManualSpan").startSpan();
// Make sure span scope is closed
try (Scope scope = span.makeCurrent()) { (1)
// Add an attribute
span.setAttribute("myAttributeName", "myValue");
// Execute logic...
return "Hello from Quarkus REST";
} catch (Exception ignored) {
// Store potential exceptions.
span.recordException(ignored);
} finally {
// Whatever happens above, the span will be closed.
span.end();
}
return "failover message";
}
}
| 1 | スパンが測定するすべてのロジックが、スコープによって明確に区切られていることが不可欠です。そのスコープは Autocloseable です。 |
手動コンテキスト伝播
OpenTelemetry コンテキストを伝播しないクライアントでメッセージまたはリクエストを送信すると、宛先がスパンを作成しても、コンテキストが伝播されなかったため、親スパンが存在しない場合に不完全なトレースまたは壊れたトレースが発生します。これにより、リクエストごとに複数のトレースが発生し、問題を引き起こします。
OpenTelemetry コンテキストを含むメタデータをメッセージまたはリクエストとともに送信する方法がある場合、これらのクライアントに対して手動伝播を実装できます。
これは高度なトピックです。コンテキスト伝播の詳細については、プロパゲーター の上記のセクションを参照してください。
次に、実装例を示します。このクラスには、MAP_SETTER、MAP_GETTER、および 2 つのメソッドが含まれています。
@ApplicationScoped
public static class HelloPropagation {
/**
* How data is stored
*/
private static final TextMapSetter<Map<String, String>> MAP_SETTER = new TextMapSetter<Map<String, String>>() {
@Override
public void set(@Nullable Map<String, String> carrier, String key, String value) {
carrier.put(key, value); (1)
}
};
/**
* How data is retrieved
*/
private static final TextMapGetter<Map<String, String>> MAP_GETTER = new TextMapGetter<>() {
@Override
public Iterable<String> keys(Map<String, String> carrier) {
return carrier == null ? emptyList() : carrier.keySet();
}
@Override
public @Nullable String get(@Nullable Map<String, String> carrier, String key) {
return carrier == null && key != null ? null : carrier.get(key); (2)
}
};
private final Tracer tracer;
private final OpenTelemetry openTelemetry;
// Instead of using @Inject on the tracer attribute
public HelloPropagation(Tracer tracer, OpenTelemetry openTelemetry) {
this.openTelemetry = openTelemetry;
// The same as openTelemetry.getTracer("io.quarkus.opentelemetry");
this.tracer = tracer;
}
public String createParentContext() { (3)
// Create the first manual span
Span parentSpan = tracer.spanBuilder("parent-span").startSpan();
try (Scope ignored = parentSpan.makeCurrent()) {
// inject into a temporary map and return the traceparent header value
// This can be stored in any data structure containing metadata in the payload to ship.
// As an example, for HTTP the headers, for JMS a message attribute
Map<String, String> tempCarrier = new HashMap<>();
// We will use The MAP_SETTER to place the header containing the OTel Context in the tempCarrier
openTelemetry.getPropagators()
.getTextMapPropagator()
.inject(Context.current(), tempCarrier, MAP_SETTER);
// W3C traceparent header key is "traceparent"
return tempCarrier.get("traceparent");
} finally {
parentSpan.end();
}
}
public void receiveAndUseContext(String traceparent) { (4)
// Rebuild a tempCarrier map that contains the "traceparent" header data
Map<String, String> tempCarrier = new HashMap<>();
tempCarrier.put("traceparent", traceparent);
// Extract context from the tempCarrier
Context extracted = openTelemetry.getPropagators()
.getTextMapPropagator()
.extract(Context.current(), tempCarrier, MAP_GETTER);
// Optionally check whether a parent span was found:
boolean hasParent = Span.fromContext(extracted).getSpanContext().isValid();
// Start a child span with the extracted context as explicit parent
Span child = tracer.spanBuilder("child-span")
.setParent(extracted)
.startSpan();
try (Scope scope = child.makeCurrent()) {
// Simulate work under child span
logger.infov("Child span started. Extracted parent valid? {0}", hasParent);
} finally {
child.end();
}
}
| 1 | MAP_SETTER は、コンテキストを転送するキャリアーマップにデータを格納する方法を処理します。 |
| 2 | MAP_GETTER は、コンテキストを転送するキャリアーマップからデータを取得する方法を処理します。 |
| 3 | createParentContext() メソッドは、OpenTelemetry コンテキストを作成する方法を説明しています。この場合、デフォルトの W3C Trace Context ヘッダーである traceparent を生成します。 |
| 4 | receiveAndUseContext() メソッドは、宛先で新しい OpenTelemetry コンテキストをブートストラップするために使用される traceparent ヘッダーの内容を受け取ります。 |
手動 Baggage 伝播
Baggage を使用して、OpenTelemetry の伝播メカニズムを使用してアプリケーション固有のデータを送信できます。
Quarkus では、使用可能なOpenTelemetry CDI依存関係注入 に記載されているように、Baggage は受信リクエストとともに来るデータをアンラップするために注入できますが、Baggage にデータを送る必要があるより複雑なシナリオでは、手動計装が必要です。
これは高度なトピックです。
前述の HelloPropagation クラスを使用して、次の 2 つのメソッド createBaggage() と receiveAndUseBaggage() を追加できます。
public Map<String, String> createBaggage(String withValue) {
// Baggage can be used to send Application specific data using the OpenTelemetry propagation mechanism
Baggage baggage = Baggage.builder()
.put("baggage_key", withValue, BaggageEntryMetadata.empty())
.build();
Context context = baggage.storeInContext(Context.current());
// We use this to store the header data to ship out.
Map<String, String> tempCarrier = new HashMap<>();
// We will use The MAP_SETTER to place the header containing the OTel Context in the tempCarrier
try (Scope ignored = context.makeCurrent()) {
openTelemetry.getPropagators()
.getTextMapPropagator()
.inject(Context.current(), tempCarrier, MAP_SETTER);
}
return tempCarrier;
}
public String receiveAndUseBaggage(Map<String, String> headers) {
// Extract context from the tempCarrier
Context extracted = openTelemetry.getPropagators()
.getTextMapPropagator()
.extract(Context.current(), headers, MAP_GETTER);
// Retrieve the baggage contents
Baggage baggage = Baggage.fromContext(extracted);
BaggageEntry baggageKey = baggage.asMap().get("baggage_key");
return baggageKey != null ? baggageKey.getValue() : null;
}
エクスポーター
メインの OpenTelemetry ガイドのエクスポーター セクションを参照してください。
OpenTelemetry Tracing で計装された Quarkus コアエクステンション
-
-
AMQP 1.0
-
RabbitMQ
-
Kafka
-
Pulsar
-
-
quarkus-vertx (httpリクエスト)
自動トレースの一部を無効にする
quarkus.otel.instrument.* プロパティーを false に設定することで、自動トレーシング計装部分を無効にすることができます。
例:
quarkus.otel.instrument.grpc=false
quarkus.otel.instrument.messaging=false
quarkus.otel.instrument.resteasy-client=false
quarkus.otel.instrument.rest=false
quarkus.otel.instrument.resteasy=false
OpenTelemetry 設定リファレンス
メインの OpenTelemetry ガイド設定 リファレンスを参照してください。