OpenTelemetry Metricsの使用
このガイドでは、Quarkus アプリケーションで OpenTelemetry (OTel) を利用して、インタラクティブなWebアプリケーションにメトリクスを提供する方法について説明します。
この技術は、previewと考えられています。 preview では、下位互換性やエコシステムでの存在は保証されていません。具体的な改善には設定や API の変更が必要になるかもしれませんが、 stable になるための計画は現在進行中です。フィードバックは メーリングリスト や GitHub の課題管理 で受け付けています。 とりうるステータスの完全なリストについては、 FAQの項目 を参照してください。 |
このドキュメントは、 QuarkusにおけるObservabilityリファレンスガイド の一部です。
|
要件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOME
が適切に設定されていること -
Apache Maven 3.9.9
-
Docker と Docker Compose、または Podman 、および Docker Compose
-
使用したい場合は、 Quarkus CLI
-
ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること
ソリューション
次の章で紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、すぐに完成した例に飛んでも構いません。
Clone the Git repository: git clone https://github.com/quarkusio/quarkus-quickstarts.git
, or download an archive.
解決策は opentelemetry-quickstart
ディレクトリー にあります。
Maven プロジェクトの作成
まず、新しいプロジェクトが必要です。 以下のコマンドで新規プロジェクトを作成します。
Windowsユーザーの場合:
-
cmdを使用する場合、(バックスラッシュ
\
を使用せず、すべてを同じ行に書かないでください)。 -
Powershellを使用する場合は、
-D
パラメータを二重引用符で囲んでください。例:"-DprojectArtifactId=opentelemetry-quickstart"
このコマンドはMavenプロジェクトを生成し、 quarkus-opentelemetry
エクステンションをインポートします。このエクステンションには、デフォルトのOpenTelemetryサポートと、 OTLPのgRPC spanエクスポーターが含まれています。
Quarkusプロジェクトがすでに設定されている場合、プロジェクトのベースディレクトリで次のコマンドを実行することで、 quarkus-opentelemetry
エクステンションをプロジェクトに追加できます:
quarkus extension add opentelemetry
./mvnw quarkus:add-extension -Dextensions='opentelemetry'
./gradlew addExtension --extensions='opentelemetry'
これにより、ビルドファイルに次の内容が追加されます。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-opentelemetry</artifactId>
</dependency>
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
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
または、JVM引数でOTLP gRPCエンドポイントを設定する場合:
quarkus dev -Djvm.args="-Dquarkus.otel.exporter.otlp.endpoint=http://localhost:4317"
./mvnw quarkus:dev -Djvm.args="-Dquarkus.otel.exporter.otlp.endpoint=http://localhost:4317"
./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;
}
}
上記の例 とほぼ同じです。
カウンター
カウンターは、負ではない、増加する値を測定するために使用できます。
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 value の attribute.name ディメンションが作成されます。 |
メトリクス名とディメンションを組み合わせると、その一意の組み合わせごとに、一意の時系列が生成されます。 無制限の次元データセット (userId などのさまざまな値) を使用すると、"カーディナリティーの急増 (cardinality explosion)"、つまり新しい時系列の作成が指数関数的に増加する可能性があります。 これは回避するようにしてください。 |
OpenTelemetry は、 LongUpDownCounter
、 DoubleCounter
、 DoubleUpDownCounter
などの他の多くのタイプのカウンターと、 ObservableLongCounter
、 ObservableDoubleCounter
、 ObservableLongUpDownCounter
、 ObservableDoubleUpDownCounter
などの 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.dice の LongHistogram を作成します。 |
2 | デフォルトのヒストグラムは Double 値を記録するため、 Long 値を記録する場合はこのビルダーメソッドが必要です。 |
3 | ヒストグラムの明示的なバケット境界を設定します。 これは、境界値も含みます。 |
4 | ロールの値を記録します。 |
5 | ヒストグラムに属性を追加します。
これにより、値が value の attribute.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
|
今後、既存の Quarkus Micrometer エクステンションメトリクスを OpenTelemetry にブリッジする予定です。
エクスポーター
メインの OpenTelemetry ガイドのエクスポーター セクションを参照してください。
OpenTelemetry 設定リファレンス
メインの OpenTelemetry ガイド設定 リファレンスを参照してください。