Application Real-Time Monitoring Service (ARMS) エージェントは、一般的な Java フレームワークを自動的にインストルメント化し、コード変更なしでトレースデータを収集します。特定のビジネスロジックを反映したトレースデータを取得するには、OpenTelemetry SDK for Java を使用してカスタムインストルメンテーションを追加できます。このトピックでは、OpenTelemetry SDK for Java を使用してカスタムインストルメンテーションを追加し、トレースコンテキストにアクセスし、カスタム Baggage を定義し、カスタム属性を設定する方法について説明します。
ARMS エージェントがサポートするコンポーネントとフレームワークの詳細については、「ARMS がサポートする Java コンポーネントとフレームワーク」をご参照ください。
前提条件
-
アプリケーションを Application Real-Time Monitoring Service (ARMS) に接続済みであること。詳細については、「アプリケーションモニタリング統合の概要」をご参照ください。
-
ARMS エージェントのバージョンが 2.9.1.2 以降であること。エージェントをアップグレードするには、「ARMS エージェントのアップグレード」をご参照ください。
依存関係の追加
プロジェクトに以下の Maven 依存関係を追加します。詳細については、「OpenTelemetry 公式ドキュメント」をご参照ください。
<dependencies>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-trace</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-bom</artifactId>
<version>1.23.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
ARMS エージェントと OpenTelemetry インストルメンテーションの互換性
基本概念
このセクションでは、一般的な用語のみを説明します。その他の用語の詳細については、OpenTelemetry 仕様をご参照ください。
-
スパン:リクエスト内の特定の操作。リモート呼び出しのエントリポイントや内部メソッド呼び出しなど。
-
SpanContext:トレースのコンテキスト。トレース ID やスパン ID などの情報を含みます。
-
属性:スパンに追加されるフィールドで、重要な情報を記録します。
-
Baggage:トレース全体を通じて伝播されるキーと値のペア。
OpenTelemetry SDK for Java の使用
OpenTelemetry SDK を使用して、以下の操作を実行できます。
-
インストルメンテーションを追加してスパンを生成する。
-
スパンに属性を追加する。
-
トレースコンテキスト内で Baggage を伝播させる。
-
現在のトレースコンテキストを取得し、トレース ID やスパン ID などの情報を出力する。
以下のサンプルコードは、OpenTelemetry SDK を使用してこれらの操作を実行する方法を示しています。
重要:OpenTelemetry インスタンスは、GlobalOpenTelemetry.get() を呼び出して取得する必要があります。OpenTelemetry SDK で手動でビルドしたインスタンスは使用しないでください。使用した場合、ARMS エージェント v4.x では、SDK インストルメンテーションによって生成されたスパンが表示されません。
@RestController
@RequestMapping("/ot")
public class OpenTelemetryController {
private Tracer tracer;
private ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
@PostConstruct
public void init() {
OpenTelemetrySdk.builder()
.setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance()))
.buildAndRegisterGlobal();
tracer = GlobalOpenTelemetry.get().getTracer("manual-sdk", "1.0.0");
ses.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
Span span = tracer.spanBuilder("schedule")
.setAttribute("schedule.time", System.currentTimeMillis())
.startSpan();
try (Scope scope = span.makeCurrent()) {
System.out.println("scheduled!");
Thread.sleep(500L);
span.setAttribute("schedule.success", true);
System.out.println(Span.current().getSpanContext().getTraceId()); // トレース ID を取得します。
} catch (Throwable t) {
span.setStatus(StatusCode.ERROR, t.getMessage());
} finally {
span.end();
}
}
}, 10, 30, TimeUnit.SECONDS);
}
@ResponseBody
@RequestMapping("/parent")
public String parent() {
Span span = tracer.spanBuilder("parent").setSpanKind(SpanKind.SERVER).startSpan();
try (Scope scope = span.makeCurrent()) {
// Baggage を使用してカスタムビジネスタグを伝播します。
Baggage baggage = Baggage.current().toBuilder()
.put("user.id", "1")
.put("user.name", "name")
.build();
try (Scope baggageScope = baggage.storeInContext(Context.current()).makeCurrent()) {
child();
}
span.setAttribute("http.method", "GET");
span.setAttribute("http.uri", "/parent");
} finally {
span.end();
}
return "parent";
}
private void child() {
Span span = tracer.spanBuilder("child").startSpan();
try (Scope scope = span.makeCurrent()) {
System.out.println("current traceId = " + Span.current().getSpanContext().getTraceId());
System.out.println("userId in baggage = " + Baggage.current().getEntryValue("user.id"));
Thread.sleep(1000);
} catch (Throwable e) {
span.setStatus(StatusCode.ERROR, e.getMessage());
} finally {
span.end();
}
}
}
サンプルコードの説明:
-
OpenTelemetryControllerのinitメソッドで、スケジュールされたタスクが開始されます。各実行の開始時にスパンが作成され、終了時に閉じられます。 -
OpenTelemetryControllerのparentメソッドでは、いくつかの OpenTelemetry SDK メソッドが呼び出されます。-
メソッドが呼び出されるたびに、
parentという名前のスパンが作成され、メソッドの終了時に閉じられます。 -
Baggage SDK を使用して、2 つの Baggage アイテム (
user.idとuser.name) を追加します。これらのアイテムは、ダウンストリームアプリケーションに伝播されます。 -
手順 2.a で作成されたスパンに 2 つの属性を追加します。
-
-
OpenTelemetryControllerのchildメソッドは、以下の操作を実行します。
ARMS エージェントのバージョン間の相違点
前述のコードの操作に対するサポートは、ARMS エージェントのバージョンによって異なります。
|
手順 |
ARMS エージェント v4.x 以降 |
ARMS エージェント v3.x 以前 |
|
1 |
サポート対象。新しいスパンが生成されます。 |
サポート対象。新しいスパンが生成されます。 |
|
2.a |
サポート対象 |
サポート対象 |
|
2.b |
サポート対象 |
サポート対象外 |
|
2.c |
サポート対象 |
サポート対象 |
|
3.a |
サポート対象 |
サポート対象。このスパンは、手順 2.a で作成されたスパン内のメソッドスタックとして表示されます。 |
|
3.b |
サポート対象。出力されるトレース ID は、ARMS のトレース ID と同じです。 |
サポート対象外。出力されるトレース ID は、ARMS エージェントのトレース ID とは異なります。 |
|
3.c |
サポート対象 |
サポート対象 |
インストルメンテーション結果
v4.x 以降
-
手順 1 のインストルメンテーション結果:
OpenTelemetry SDK によって生成されたスパンを確認できます。
ARMS コンソールのトレース詳細にあるスパン詳細では、アプリケーション名 (例:
elastic-search-8)、操作名 (例:schedule)、スパンタイプ (INTERNAL)、期間などの基本情報を確認できます。[Attributes] タブでは、otel.scope.name=manual-sdkやotel.scope.version=1.0.0などの OpenTelemetry 属性、およびschedule.success=trueなどのカスタムビジネス属性を確認でき、インストルメンテーションが機能していることを確認できます。 -
手順 2.x および手順 3.x のインストルメンテーション結果:
OpenTelemetry SDK によって生成されたスパンは、エージェントによって生成された Tomcat スパンと同じトレース内に表示されます。さらに、SDK で生成されたスパンの関連属性が期待どおりに設定されます。
トレース詳細では、Tomcat エージェントインストルメンテーションによって生成されたスパンの操作名は
/opentelemetry/parentです。OpenTelemetry SDK によって生成されたスパンは、parentとその子スパンchildという名前です。スパン属性では、http.methodはGET、http.uriは/parent、カスタム属性otel.scope.nameはmanual-sdkです。
v3.x 以前
-
手順 1 のインストルメンテーション結果:
ARMS コンソールのトレース詳細ページで、インストルメンテーションによって収集されたトレースデータを確認できます。左側のウォーターフォールチャートには、スパンの呼び出し関係が表示されます。たとえば、
elastic-search-9アプリケーションのscheduleメソッドは、コンポーネントタイプがuser_methodで、合計応答時間が 567 ms のスパンとしてキャプチャされます。右側の [Span Details] パネルには、アプリケーション名、スパン名、IP アドレス、スパン ID、ステータスコードなどの基本情報が表示されます。[Attributes] セクションには、HTTP 情報 (http.path、http.status_code)、RPC 情報 (rpc.type)、組み込み情報 (component.name=user_method、slow=1) といった、スパンの種類に応じた追加属性がグループ化されて表示されます。 -
手順 2.x および手順 3.x のインストルメンテーション結果:
childスパンは、parentスパンのメソッドスタック内に表示されます。さらに、SDK で生成されたスパンの属性が期待どおりに設定されます。[メソッドスタック] ビューでは、トレース階層に OpenTelemetry Entry Span が parent スパンを呼び出し、それが child スパンを呼び出す様子が表示されます。これら 3 つのネストされた呼び出しは、それぞれ約 1.01 秒かかります。child メソッドの属性には、
line=-1が含まれます。
関連ドキュメント
トレース ID 情報をアプリケーションのビジネスログと関連付けることで、問題が発生した際に関連するログをすばやく見つけてトラブルシューティングを行うことができます。詳細については、「Java アプリケーションのトレース ID とビジネスログの関連付け」をご参照ください。