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 Specification」をご参照ください。
-
スパン:リクエスト内の特定の操作。リモート呼び出しのエントリポイントや内部メソッド呼び出しなど。
-
SpanContext:トレースのコンテキスト。トレース ID やスパン ID などの情報を含む。
-
属性:スパン上の追加フィールド。重要な情報を記録する。
-
Baggage:トレース全体を通じて伝播されるキーと値のペア。
OpenTelemetry SDK for Java の使用
OpenTelemetry SDK を使用して、次の操作を実行できます。
-
インストルメンテーションを追加してスパンを生成する。
-
スパンに属性を追加する。
-
トレースコンテキスト内で Baggage を伝播させる。
-
現在のトレースコンテキストを取得し、トレース ID やスパン ID などの情報を出力する。
次のコード例は、OpenTelemetry SDK を使用してこれらの操作を実行する方法を示しています。
重要:GlobalOpenTelemetry.get() を呼び出して OpenTelemetry インスタンスを取得する必要があります。OpenTelemetry SDK を使用して手動で構築したインスタンスは使用しないでください。使用した場合、ARMS エージェント v4.x では、SDK インストルメンテーションによって生成されたスパンが表示されません。
@RestController
@RequestMapping("/ot")
public class OpenTelemetryController {
private Tracer tracer;
private ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
@PostConstruct
public void init() {
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 を使用して、
user.idとuser.nameの 2 つの Baggage アイテムを追加します。これらのアイテムは下流のアプリケーションに伝播されます。 -
手順 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 コンソールのトレースの [Span Details] では、アプリケーション名 (例:
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 Information (http.path、http.status_code)、RPC Information (rpc.type)、Built-in Information (component.name=user_method、slow=1) などのグループ別に追加の属性が表示されます。 -
手順 2.x および手順 3.x のインストルメンテーション結果:
childスパンは、parentスパンのメソッドスタック内に表示されます。また、SDK によって生成されたスパンの属性は、期待どおりに設定されています。[メソッドスタック] ビューでは、トレース階層に [OpenTelemetry Entry Span] が [parent] スパンを呼び出し、さらに [child] スパンを呼び出していることが示されています。これらの 3 つのネストされた呼び出しは、それぞれ約 1.01 秒かかります。[child] メソッドの属性には、
line=-1およびrpc.type=98が含まれています。
関連ドキュメント
トレース ID 情報をアプリケーションのビジネスログと関連付けることで、問題が発生した際に関連するログをすばやく見つけてトラブルシューティングを行うことができます。詳細については、「Java アプリケーションのトレース ID とビジネスログの関連付け」をご参照ください。