このブログシリーズでは、複数の言語にまたがるOpenTelemetry標準を使用した分散トレースのアプリケーションインストルメンテーション手順について説明します。 以前にカバーしました 分散トレース用のGolangアプリケーションインストルメンテーション および 分散トレース用のDotNetアプリケーションインストルメンテーション。 ここでは、Javaのインストルメンテーションについて説明します。
新しいプロジェクトを初期化する
まず、新しいJavaプロジェクトを作成し、以下に必要な依存関係を追加します。 OpenTelemetry 手動計装。
達人
<project>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-bom</artifactId>
<version>1.2.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-semconv</artifactId>
<version>1.5.0-alpha</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.39.0</version>
</dependency>
</dependencies>
</project>
受け台
dependencies {
implementation platform("io.opentelemetry:opentelemetry-bom:1.2.0")
implementation('io.opentelemetry:opentelemetry-api')
implementation('io.opentelemetry:opentelemetry-sdk')
implementation('io.opentelemetry:opentelemetry-exporter-otlp')
implementation('io.opentelemetry:opentelemetry-semconv:1.5.0-alpha')
implementation('io.grpc:grpc-netty-shaded:1.39.0')
}
OpenTelemetry BOMを使用して、さまざまなコンポーネントのバージョンの同期を維持することをお勧めします。
他の最終的なアプリケーションで使用されるライブラリを開発している場合、コードはopentelemetry-apiにのみ依存します。
リソース検出器を作成する
リソースは、を生成したオブジェクトを記述します テレメトリー 信号。 基本的に、これはサービスまたはアプリケーションの名前である必要があります。 OpenTelemetryは、サービス実行環境を説明するための標準を定義しています。 ホスト名、hostType(クラウド、コンテナー、サーバーレス)、名前空間、cloud-resource-idなど。これらの属性は以下で定義されます。 リソースセマンティックコンベンション またはsemconv。
ここでは、いくつかの環境属性を持つリソースを作成します。
属性 | 商品説明 | 必須 |
サービス名 | これは、サービスの論理名です。 | あり |
service.namespace | サービスをグループ化するために使用されます。たとえば、service.namespaceを使用して、QA、UAT、PRODなどの環境間でサービスを区別できます。 | いいえ |
ホスト名 | サービスが実行されているホストの名前。 | いいえ |
//Create Resource
AttributesBuilder attrBuilders = Attributes.builder()
.put(ResourceAttributes.SERVICE_NAME, SERVICE_NAME)
.put(ResourceAttributes.SERVICE_NAMESPACE, "US-West-1")
.put(ResourceAttributes.HOST_NAME, "prodsvc.us-west-1.example.com");
Resource serviceResource = Resource
.create(attrBuilders.build());
Init SpanExporter
エクスポータは、アプリケーションからリモートバックエンドへのテレメトリ信号(トレース)のエクスポート、ファイルへのログ、stdoutへのストリームなどを担当するSDKのコンポーネントです。
この例では、localhost:55680で実行されているOTLPレシーバーバックエンドにトレースを送信するgRPCエクスポーターを作成しています。 おそらくOTELコレクター。
//Create Span Exporter
OtlpGrpcSpanExporter spanExporter = OtlpGrpcSpanExporter.builder()
.setEndpoint("http://localhost:55680")
.build();
TracerProviderを構築し、SDKを構成します
TracerProviderを使用すると、スパンの作成に使用されるTracerにアクセスできます。
//Create SdkTracerProvider
SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()
.addSpanProcessor(BatchSpanProcessor.builder(spanExporter)
.setScheduleDelay(100, TimeUnit.MILLISECONDS).build())
.setResource(serviceResource)
.build();
//This Instance can be used to get tracer if it is not configured as global
OpenTelemetry openTelemetry = OpenTelemetrySdk.builder()
.setTracerProvider(sdkTracerProvider)
.buildAndRegisterGlobal();
アプリケーションの最初のステップとして、SDKを構成し、トレーサーを作成する必要があります。
トレーサーを作成する
Tracer tracer= GlobalOpenTelemetry.getTracer("auth-Service-instrumentation");
//Tracer tracer= GlobalOpenTelemetry.getTracer("auth-Service-instrumentation","1.0.0");
//OR use the OpenTelemetry instance from previous step to get tracer
//openTelemetry.getTracer("auth-Service-instrumentation");
GlobalOpenTelemetryは、前の手順でOpenTelemeryインスタンスがグローバルとして登録されている場合にのみ使用できます。それ以外の場合は、SDKビルダーから返されたOpenTelemetryインスタンスを使用できます。
getTracerメソッドでは、パラメーターとしてインストルメンテーションライブラリ名が必要です。これはnullであってはなりません。
スパンを作成し、スパン属性を定義する
スパンは、操作のXNUMX回の実行です。 これは、スパンタグと呼ばれることもある一連の属性によって識別されます。 アプリケーションの所有者は、スパンに必要な情報を取得できる属性を自由に選択できます。 スパンごとのスパン属性の数に制限はありません。
この例では、サンプルアプリケーションのXNUMXスパン属性を定義しています。
Span parentSpan = tracer.spanBuilder("doLogin").startSpan();
parentSpan.setAttribute("priority", "business.priority");
parentSpan.setAttribute("prodEnv", true);
子スパンを作成する
setParentメソッドを使用して、スパンを手動で相互に関連付けることができます。
Span childSpan = tracer.spanBuilder("child")
.setParent(Context.current().with(parentSpan))
.startSpan();
OpenTelemetry APIは、現在のスレッドで親スパンを伝播する自動化された方法も提供します。
makeCurrentメソッドを使用して、現在のスレッドで親スパンを自動的に伝播します。
try (Scope scope = parentSpan.makeCurrent()) {
Thread.sleep(200);
boolean isValid=isValidAuth(username,password);
//Do login
} catch (Throwable t) {
parentSpan.setStatus(StatusCode.ERROR, "Change it to your error message");
} finally {
parentSpan
.end(); // closing the scope does not end the span, this has to be done manually
}
//Child Method
private boolean isValidAuth(String username,String password){
Span childSpan = tracer.spanBuilder("isValidAuth").startSpan();
// NOTE: setParent(...) is not required;
// `Span.current()` is automatically added as the parent
childSpan.setAttribute("Username", username)
.setAttribute("id", 101);
//Auth code goes here
try {
Thread.sleep(200);
childSpan.setStatus(StatusCode.OK);
} catch (InterruptedException e) {
childSpan.setStatus(StatusCode.ERROR, "Change it to your error message");
}finally {
childSpan.end();
}
return true;
}
スパンへのイベント/ログの追加
スパンは、スパンの実行中に発生したいくつかの実行ログ/イベントで強化できます。 この情報は、常にそれぞれのスパンに関連付けられたコンテキストログを提供するのに役立ちます。
Attributes eventAttributes = Attributes.builder().put("Username", username)
.put("id", 101).build();
childSpan.addEvent("User Logged In", eventAttributes);
それをまとめる
TestApplication.java
package com.logicmonitor.example;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.util.concurrent.TimeUnit;
public class TestApplication {
private static final String SERVICE_NAME = "Authentication-Service";
static {
//Create Resource
AttributesBuilder attrBuilders = Attributes.builder()
.put(ResourceAttributes.SERVICE_NAME, SERVICE_NAME)
.put(ResourceAttributes.SERVICE_NAMESPACE, "US-West-1")
.put(ResourceAttributes.HOST_NAME, "prodsvc.us-west-1.example.com");
Resource serviceResource = Resource
.create(attrBuilders.build());
//Create Span Exporter
OtlpGrpcSpanExporter spanExporter = OtlpGrpcSpanExporter.builder()
.setEndpoint("http://localhost:55680")
.build();
//Create SdkTracerProvider
SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()
.addSpanProcessor(BatchSpanProcessor.builder(spanExporter)
.setScheduleDelay(100, TimeUnit.MILLISECONDS).build())
.setResource(serviceResource)
.build();
//This Instance can be used to get tracer if it is not configured as global
OpenTelemetry openTelemetry = OpenTelemetrySdk.builder()
.setTracerProvider(sdkTracerProvider)
.buildAndRegisterGlobal();
}
public static void main(String[] args) throws InterruptedException {
Auth auth = new Auth();
auth.doLogin("testUserName", "testPassword");
Thread.sleep(1000);
}
}
Auth.Java
package com.logicmonitor.example;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;
public class Auth {
Tracer tracer = GlobalOpenTelemetry.getTracer("auth-Service-instrumentation");
//Tracer tracer= GlobalOpenTelemetry.getTracer("auth-Service-instrumentation","1.0.0");
public void doLogin(String username, String password) {
Span parentSpan = tracer.spanBuilder("doLogin").startSpan();
parentSpan.setAttribute("priority", "business.priority");
parentSpan.setAttribute("prodEnv", true);
try (Scope scope = parentSpan.makeCurrent()) {
Thread.sleep(200);
boolean isValid = isValidAuth(username, password);
//Do login
} catch (Throwable t) {
parentSpan.setStatus(StatusCode.ERROR, "Change it to your error message");
} finally {
parentSpan
.end(); // closing the scope does not end the span, this has to be done manually
}
}
private boolean isValidAuth(String username, String password) {
Span childSpan = tracer.spanBuilder("isValidAuth").startSpan();
// NOTE: setParent(...) is not required;
// `Span.current()` is automatically added as the parent
//Auth code goes here
try {
Thread.sleep(200);
childSpan.setStatus(StatusCode.OK);
Attributes eventAttributes = Attributes.builder().put("Username", username)
.put("id", 101).build();
childSpan.addEvent("User Logged In", eventAttributes);
} catch (InterruptedException e) {
childSpan.setStatus(StatusCode.ERROR, "Change it to your error message");
} finally {
childSpan.end();
}
return true;
}
}
アプリケーションを実行する
TestApplication.javaを実行します。
LogicMonitorプラットフォームで受信したトレース

トレースの詳細ビュー
親スパン:

子スパン:

まとめ
おめでとうございます。OpenTelemetryProtocol(OTLP)仕様を使用してトレースを発行するJavaアプリケーションを作成しました。 OTLP仕様を使用してビジネスアプリケーションのインストルメント化を開始するときは、このコードを参照として自由に使用してください。 LogicMonitor APM仕様は、ベンダーロックインなしで100%OTLPに準拠しています。 LogicMonitorプラットフォームでトラブルシューティングするための複数のサービスのトレースを受信して視覚化するには、無料の試用アカウントにサインアップしてください (詳細はこちら)。 複数の言語にわたるOpenTelemetry標準を使用した分散トレースのアプリケーションインストルメンテーション手順をカバーするブログをチェックしてください。