The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.
このページを編集

OpenTelemetry Metricsの使用

このガイドでは、Quarkus アプリケーションで OpenTelemetry (OTel) を利用して、インタラクティブなWebアプリケーションにメトリクスを提供する方法について説明します。

この技術は、previewと考えられています。

preview では、下位互換性やエコシステムでの存在は保証されていません。具体的な改善には設定や API の変更が必要になるかもしれませんが、 stable になるための計画は現在進行中です。フィードバックは メーリングリストGitHub の課題管理 で受け付けています。

とりうるステータスの完全なリストについては、 FAQの項目 を参照してください。

このドキュメントは、 QuarkusにおけるObservabilityリファレンスガイド の一部です。

  • OpenTelemetry メトリクスは テクノロジープレビュー とみなされており、デフォルトでは無効になっています。

  • OpenTelemetry ガイド には、信号に依存しない、OpenTelemetry エクステンションに関する情報が記載されています。

  • OpenTelemetry Tracing の詳細情報を確認する場合は、OpenTelemetry Tracing ガイド を参照してください。

要件

このガイドを完成させるには、以下が必要です:

  • 約15分

  • IDE

  • JDK 17+がインストールされ、 JAVA_HOME が適切に設定されていること

  • Apache Maven 3.9.9

  • Docker と Docker Compose、または Podman 、および Docker Compose

  • 使用したい場合は、 Quarkus CLI

  • ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること

アーキテクチャー

このガイドでは、分散トレースを実証するための簡単なRESTアプリケーションを作成します。

ソリューション

次の章で紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、すぐに完成した例に飛んでも構いません。

Clone the Git repository: git clone https://github.com/quarkusio/quarkus-quickstarts.git, or download an archive.

解決策は opentelemetry-quickstart ディレクトリー にあります。

Maven プロジェクトの作成

まず、新しいプロジェクトが必要です。 以下のコマンドで新規プロジェクトを作成します。

コマンドラインインタフェース
quarkus create app org.acme:opentelemetry-quickstart \
    --extension='rest,quarkus-opentelemetry' \
    --no-code
cd opentelemetry-quickstart

Gradleプロジェクトを作成するには、 --gradle または --gradle-kotlin-dsl オプションを追加します。

Quarkus CLIのインストールと使用方法の詳細については、 Quarkus CLI ガイドを参照してください。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.19.4:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=opentelemetry-quickstart \
    -Dextensions='rest,quarkus-opentelemetry' \
    -DnoCode
cd opentelemetry-quickstart

Gradleプロジェクトを作成するには、 -DbuildTool=gradle または -DbuildTool=gradle-kotlin-dsl オプションを追加します。

Windowsユーザーの場合:

  • cmdを使用する場合、(バックスラッシュ \ を使用せず、すべてを同じ行に書かないでください)。

  • Powershellを使用する場合は、 -D パラメータを二重引用符で囲んでください。例: "-DprojectArtifactId=opentelemetry-quickstart"

このコマンドはMavenプロジェクトを生成し、 quarkus-opentelemetry エクステンションをインポートします。このエクステンションには、デフォルトのOpenTelemetryサポートと、 OTLPのgRPC spanエクスポーターが含まれています。

Quarkusプロジェクトがすでに設定されている場合、プロジェクトのベースディレクトリで次のコマンドを実行することで、 quarkus-opentelemetry エクステンションをプロジェクトに追加できます:

コマンドラインインタフェース
quarkus extension add opentelemetry
Maven
./mvnw quarkus:add-extension -Dextensions='opentelemetry'
Gradle
./gradlew addExtension --extensions='opentelemetry'

これにより、ビルドファイルに次の内容が追加されます。

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-opentelemetry</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-opentelemetry")

Jakarta REST リソースの調査

src/main/java/org/acme/opentelemetry/MetricResource.java のファイルを開くと、以下の内容が表示されます。

package org.acme;

import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.Meter;
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-metrics")
public class MetricResource {

    private static final Logger LOG = Logger.getLogger(MetricResource.class);

    private final LongCounter counter;

    public MetricResource(Meter meter) { (1)
        counter = meter.counterBuilder("hello-metrics") (2)
                .setDescription("hello-metrics")
                .setUnit("invocations")
                .build();
    }

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        counter.add(1); (3)
        LOG.info("hello-metrics");
        return "hello-metrics";
    }
}

Quarkus では現在、すぐに使用できるメトリクスは生成されません。 ここでは、 hello() メソッドの呼び出し回数のカウンターを作成しています。

1 Meter インスタンスのコンストラクター注入。
2 説明と単位を指定して、 hello-metrics という名前の LongCounter を作成します。
3 hello() メソッドが呼び出されるたびに、カウンターを 1 ずつ増やします。

構成の作成

OpenTelemetry メトリクスの唯一の必須設定は、これを有効にする設定です。

quarkus.otel.metrics.enabled=true

デフォルトのプロパティ値を変更するために、 src/main/resources/application.properties ファイルを使用して、アプリケーション内でデフォルトの OTLP gRPC Exporter を設定する方法の例を以下に示します:

quarkus.application.name=myservice (1)
quarkus.otel.metrics.enabled=true (2)
quarkus.otel.exporter.otlp.metrics.endpoint=http://localhost:4317 (3)
quarkus.otel.exporter.otlp.metrics.headers=authorization=Bearer my_secret (4)
1 アプリケーションから作成されたすべてのメトリクスには、メトリクスが myservice アプリケーションによって作成されたことを示す OpenTelemetry Resource が含まれます。 設定されていない場合は、デフォルトでアーティファクト ID になります。
2 OpenTelemetry メトリクスを有効にします。 ビルド時に設定する必要があります。
3 メトリクスを送信するための gRPC エンドポイント。 設定されていない場合、デフォルトは http://localhost:4317 です。
4 認証によく使われるオプションの gRPC ヘッダー

すべての信号に対して同じプロパティーを使用して接続を設定するには、ベースの OpenTelemetry ガイドの設定セクション を確認してください。

OpenTelemetry の特定の部分を無効にするには、この OpenTelemetry ガイドのセクション にリストされているプロパティーを設定できます。

アプリケーションの実行

まず、OpenTelemetry データを視覚化するシステムを起動する必要があります。

Grafana-OTel-LGTM Dev サービスのデータを参照してください。

Grafana-OTel-LGTM Dev Service

Grafana-OTel-LGTM devservice を使用できます。

この開発サービスには、データを視覚化する Grafana、ログを保存する Loki、トレースを保存する Tempo、メトリクスを保存する Prometheus が含まれています。 また、データを受信する OTel コレクターも提供します。

Logging エクスポーター

application.properties ファイルでエクスポーターを logging に設定して、すべてのメトリクスをコンソールに出力できます。

quarkus.otel.metrics.exporter=logging (1)
quarkus.otel.metric.export.interval=10000ms (2)
1 エクスポーターを logging に設定します。 通常、これを設定する必要はありません。 デフォルトは cdi です。
2 メトリクスのエクスポート間隔を設定します。 デフォルトは 1m ですが、デバッグには長すぎます。

次の依存関係もプロジェクトに追加します。

<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-logging</artifactId>
</dependency>

アプリケーションを起動します。

これでアプリケーションを実行する準備が整いました。 トレーサーの設定に application.properties を使用している場合:

コマンドラインインタフェース
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

または、JVM引数でOTLP gRPCエンドポイントを設定する場合:

コマンドラインインタフェース
quarkus dev -Djvm.args="-Dquarkus.otel.exporter.otlp.endpoint=http://localhost:4317"
Maven
./mvnw quarkus:dev -Djvm.args="-Dquarkus.otel.exporter.otlp.endpoint=http://localhost:4317"
Gradle
./gradlew --console=plain quarkusDev -Djvm.args="-Dquarkus.otel.exporter.otlp.endpoint=http://localhost:4317"

OpenTelemetry Collector、Jaegerシステム、アプリケーションが動作している状態で、提供されているエンドポイントにリクエストを出すことができます。

$ curl http://localhost:8080/hello-metrics
hello-metrics

ロガーエクスポーターを使用すると、メトリクスがコンソールに出力されます。 以下は pretty-print の例です。

{
  "metric": "ImmutableMetricData",
  "resource": {
    "Resource": {
      "schemaUrl": null,
      "attributes": { (1)
        "host.name": "myhost",
        "service.name": "myservice ",
        "service.version": "1.0.0-SNAPSHOT",
        "telemetry.sdk.language": "java",
        "telemetry.sdk.name": "opentelemetry",
        "telemetry.sdk.version": "1.32.0",
        "webengine.name": "Quarkus",
        "webengine.version": "999-SNAPSHOT"
      }
    },
    "instrumentationScopeInfo": {
      "InstrumentationScopeInfo": { (2)
        "name": "io.quarkus.opentelemetry",
        "version": null,
        "schemaUrl": null,
        "attributes": {}
      }
    },
    "name": "hello-metrics", (3)
    "description": "hello-metrics",
    "unit": "invocations",
    "type": "LONG_SUM",
    "data": {
      "ImmutableSumData": {
        "points": [
          {
            "ImmutableLongPointData": {
              "startEpochNanos": 1720622136612378000,
              "epochNanos": 1720622246618331000,
              "attributes": {},
              "value": 3, (4)
              "exemplars": [ (5)
                {
                  "ImmutableLongExemplarData": {
                    "filteredAttributes": {},
                    "epochNanos": 1720622239362357000,
                    "spanContext": {
                      "ImmutableSpanContext": {
                        "traceId": "d91951e50b0641552a76889c5356467c",
                        "spanId": "168af8b7102d0556",
                        "traceFlags": "01",
                        "traceState": "ArrayBasedTraceState",
                        "entries": [],
                        "remote": false,
                        "valid": true
                      },
                      "value": 1
                    }
                  }
                }
              ]
            }
          }
        ],
        "monotonic": true,
        "aggregationTemporality": "CUMULATIVE"
      }
    }
  }
}
1 すべてのテレメトリーデータに共通するリソース属性。
2 計装スコープは常に io.quarkus.opentelemetry です。
3 MetricResource クラスのコンストラクターで定義したメトリクスの名前、説明、および単位。
4 メトリクスの値。 これまでに 3 回の呼び出しが行われました。
5 メトリクスに関する追加のトレース情報の例。 この場合、traceId と spanId は、最後に送信されてからメトリクスをトリガーしたリクエストのいずれかになります。

CTRL+C を押すか、 q と入力して、アプリケーションを停止してください。

独自のメトリクスの作成

OpenTelemetry メトリクスと Micrometer メトリクス

メトリクスは単一の数値測定値であり、多くの場合、その値とともにキャプチャーされた追加データも含まれています。 この補助データを使用し、メトリクスをグループ化または集計して分析します。

Quarkus Micrometer エクステンション とほぼ同様に、OpenTelemetry API を使用して独自のメトリクスを作成でき、概念も同じです。

OpenTelemetry API は、レジストリーの代わりにメトリクスを作成するための Meter インターフェイスを提供します。 Meter インターフェイスは、メトリクスを作成するエントリーポイントです。 このインターフェイスにより、カウンター、ゲージ、ヒストグラムの作成メソッドが提供されます。

Micrometer のタグとほぼ同じように、メトリクスに属性を追加してディメンションを追加できます。

Meter への参照の取得

メーターへの参照を取得するには、次のいずれかの方法を使用します。

CDIコンストラクタ注入の使用

@Path("/hello-metrics")
public class MetricResource {

    private final Meter meter;

    public MetricResource(Meter meter) {
        this.meter = meter;
    }
}

上記の例 とほぼ同じです。

@Inject アノテーションを使用したメンバー変数

@Inject
Meter meter;

カウンター

カウンターは、負ではない、増加する値を測定するために使用できます。

LongCounter counter = meter.counterBuilder("hello-metrics") (1)
        .setDescription("hello-metrics")                    // optional
        .setUnit("invocations")                             // optional
        .build();

counter.add(1, (2)
        Attributes.of(AttributeKey.stringKey("attribute.name"), "attribute value")); // optional (3)
1 説明と単位を指定して、 hello-metrics という名前の LongCounter を作成します。
2 カウンターを 1 増やします。
3 カウンターに属性を追加します。 これにより、値が attribute valueattribute.name ディメンションが作成されます。
メトリクス名とディメンションを組み合わせると、その一意の組み合わせごとに、一意の時系列が生成されます。 無制限の次元データセット (userId などのさまざまな値) を使用すると、"カーディナリティーの急増 (cardinality explosion)"、つまり新しい時系列の作成が指数関数的に増加する可能性があります。 これは回避するようにしてください。

OpenTelemetry は、 LongUpDownCounterDoubleCounterDoubleUpDownCounter などの他の多くのタイプのカウンターと、 ObservableLongCounterObservableDoubleCounterObservableLongUpDownCounterObservableDoubleUpDownCounter などの Observable の非同期カウンターも提供します。

詳細は、 カウンターに関する OpenTelemetry Java ドキュメント を参照してください。

ゲージ

非加算値を測定するには、観測可能なゲージを使用する必要があります。 車のスピードメーターのように、時間の経過とともに増加または減少する値です。 ゲージは、キャッシュまたはコレクションの統計を監視するときに役立ちます。

このメトリクスを使用すると、コールバックによって定期的にプローブされる関数が提供されます。 関数によって返される値はゲージの値です。

デフォルトのゲージは Double 値を記録しますが、Long 値を記録する場合は、以下を使用できます。

meter.gaugeBuilder("jvm.memory.total")                      (1)
        .setDescription("Reports JVM memory usage.")
        .setUnit("byte")
        .ofLongs()                                          (2)
        .buildWithCallback(                                 (3)
                result -> result.record(
                        Runtime.getRuntime().totalMemory(), (4)
                        Attributes.empty()));               // optional (5)
1 説明と単位を指定して、名前に jvm.memory.total を指定して Gauge を作成します。
2 デフォルトのゲージは Double 値を記録するため、 Long 値を記録する場合はこのビルダーメソッドが必要です。
3 コールバックを使用してゲージを構築します。 命令型ビルダーも利用可能です。
4 ゲージの値を取得するために呼び出す関数を登録します。
5 今回は属性は追加されません。

ヒストグラム

ヒストグラムは、時間の経過に伴う値の分布を測定するために使用される同期計装です。 ヒストグラム、サマリー、パーセンタイルなどの統計を目的としています。 要求の継続時間と応答ペイロードサイズは、ヒストグラムでの使用に適しています。

このセクションには、る新しいクラス HistogramResource があり、このクラスは LongHistogram を作成します。

package org.acme;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.LongHistogram;
import io.opentelemetry.api.metrics.Meter;
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;

import java.util.Arrays;

@Path("/roll-dice")
public class HistogramResource {

    private static final Logger LOG = Logger.getLogger(HistogramResource.class);

    private final LongHistogram rolls;

    public HistogramResource(Meter meter) {
        rolls = meter.histogramBuilder("hello.roll.dice")  (1)
                .ofLongs()                                 (2)
                .setDescription("A distribution of the value of the rolls.")
                .setExplicitBucketBoundariesAdvice(Arrays.asList(1L, 2L, 3L, 4L, 5L, 6L, 7L)) (3)
                .setUnit("points")
                .build();
    }

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String helloGauge() {
        var roll = roll();
        rolls.record(roll,                                 (4)
                Attributes.of(AttributeKey.stringKey("attribute.name"), "value"));            (5)
        LOG.info("roll-dice: " + roll);
        return "" + roll;
    }

    public long roll() {
        return (long) (Math.random() * 6) + 1;
    }
}
1 説明と単位を指定して、名前が hello.roll.diceLongHistogram を作成します。
2 デフォルトのヒストグラムは Double 値を記録するため、 Long 値を記録する場合はこのビルダーメソッドが必要です。
3 ヒストグラムの明示的なバケット境界を設定します。 これは、境界値も含みます。
4 ロールの値を記録します。
5 ヒストグラムに属性を追加します。 これにより、値が valueattribute.name ディメンションが作成されます。
カーディナリティーの急増 (cardinality explosion) に注意してください。

curl コマンドを使用してエンドポイントを呼び出すことができます。

$ curl http://localhost:8080/roll-dice
2

結果が 2、2、3、4 である 4 つのリクエストを連続して実行すると、次の出力が生成されます。 Resource および InstrumentationScopeInfo データは無視し、簡素化しています。

//...
name=hello.roll.dice,
description=A distribution of the value of the rolls.,      (1)
unit=points,
type=HISTOGRAM,
data=ImmutableHistogramData{
    aggregationTemporality=CUMULATIVE,                      (2)
    points=[
        ImmutableHistogramPointData{
            getStartEpochNanos=1720632058272341000,
            getEpochNanos=1720632068279567000,
            getAttributes={attribute.name="value"},         (3)
            getSum=11.0,       (4)
            getCount=4,        (5)
            hasMin=true,
            getMin=2.0,        (6)
            hasMax=true,
            getMax=4.0,        (7)
            getBoundaries=[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0],   (8)
            getCounts=[0, 2, 1, 1, 0, 0, 0, 0],                  (9)
            getExemplars=[     (10)
                ImmutableDoubleExemplarData{
                    filteredAttributes={},
                    epochNanos=1720632063049392000,
                    spanContext=ImmutableSpanContext{
                        traceId=a22b43a600682ca7320516081eca998b,
                        spanId=645aa49f219181d0,
                        traceFlags=01,
                        traceState=ArrayBasedTraceState{entries=[]},
                        remote=false,
                        valid=true
                    },
                    value=2.0  (11)
                },
                //... exemplars for values 3 and 4 omitted for brevity
            ]
        }
    ]
}
1 HistogramResource クラスのコンストラクターで定義したメトリクスの名前、説明、および単位。
2 ヒストグラムの集計一時性。
3 値の記録時にヒストグラムに追加された属性。
4 記録された値の合計。
5 記録された値の数。
6 記録された最小値。
7 記録された最大値。
8 ヒストグラムの明示的なバケット境界。
9 各バケットに記録された値の数。
10 記録された値のトレースデータを含む例のリスト。 3 つの例のうち 1 つだけを示し、簡潔にまとめています。
11 値 2 で行われた 2 回の呼び出しのうちの 1 つ。

Micrometer API との相違点

  • タイマーおよびディストリビューションの概要は OpenTelemetry API では使用できません。 代わりに、ヒストグラムを使用します。

  • OpenTelemetry API は、Micrometer の @Counted@Timed@MeterTag などのメトリクスのアノテーションを定義しません。 メトリクスを手動で作成する必要があります。

  • OpenTelemetry は、メトリクスと属性に名前を付けるために独自の セマンティック規則 を使用します。

リソース

メインの OpenTelemetry ガイドリソース セクションを参照してください。

自動計装

Quarkus では、 Microprofile メトリクス 2.0 仕様 に従って、JVM メトリクスと HTTP サーバー要求メトリクスを自動計装します。

これらのメトリクスは、次のプロパティーを false に設定することで無効にできます。

quarkus.otel.instrument.jvm-metrics=false
quarkus.otel.instrument.http-server-metrics=false
  • Micrometer エクステンションも使用している場合は、これらの計装を無効にすることを推奨します。

今後、既存の Quarkus Micrometer エクステンションメトリクスを OpenTelemetry にブリッジする予定です。

エクスポーター

メインの OpenTelemetry ガイドのエクスポーター セクションを参照してください。

OpenTelemetry 設定リファレンス

メインの OpenTelemetry ガイド設定 リファレンスを参照してください。

関連コンテンツ