分散トレース用のJavaアプリケーションマニュアルインスツルメンテーション

分散トレース用のJavaアプリケーションマニュアルインスツルメンテーション

このブログシリーズでは、複数の言語にまたがる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プラットフォームで受信したトレース

トレースはLogicMonitorで実行されます

トレースの詳細ビュー

親スパン:

Logicmonitorプラットフォームのトレースセクション内の親スパン。

子スパン:

LogicMonitorのトレースの子スパン

結論

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

ガウランパテル

ガウランパテル

シニアソフトウェアエンジニア

Gaurang Patelは、LogicMonitorの従業員です。