The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

マイクロメータを用いたメトリクスの収集

Micrometer メトリクスライブラリーを使用して、ランタイム、エクステンション、アプリケーションのメトリクスを収集し、Prometheus (OpenMetrics) エンドポイントとして公開するアプリケーションを作成します。

前提条件

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

  • 約15分

  • IDE

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

  • Apache Maven 3.9.6

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

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

ソリューション

手順に沿ってアプリケーションを作成することをお勧めしますが、必要に応じてソリューションに進むことも可能です。以下のいずれかを実行します。

ソリューションは micrometer-quickstart ディレクトリ にあります。

1. Maven プロジェクトの作成

以下のコマンドで新規プロジェクトを作成します。

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

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

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

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

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

Windowsユーザーの場合:

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

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

このコマンドは、 micrometer-registry-prometheus エクステンションを依存関係としてインポートする Maven プロジェクトを生成します。このエクステンションは、 micrometer エクステンションだけでなく、 Prometheus をサポートするために必要な追加のライブラリ依存関係もロードします。

2. RESTエンドポイントの作成

まず、素数を計算する単純なエンドポイントを追加してみましょう。

package org.acme.micrometer;

import java.util.LinkedList;
import java.util.NoSuchElementException;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.Timer;

@Path("/example")
@Produces("text/plain")
public class ExampleResource {

    @GET
    @Path("prime/{number}")
    public String checkIfPrime(@PathParam("number") long number) {
        if (number < 1) {
            return "Only natural numbers can be prime numbers.";
        }
        if (number == 1) {
            return number + " is not prime.";
        }
        if (number == 2 || number % 2 == 0) {
            return number + " is not prime.";
        }
        if (testPrimeNumber(number)) {
            return number + " is prime.";
        } else {
            return number + " is not prime.";
        }
    }

    protected boolean testPrimeNumber(long number) {
        for (int i = 3; i < Math.floor(Math.sqrt(number)) + 1; i = i + 2) {
            if (number % i == 0) {
                return false;
            }
        }
        return true;
    }
}

アプリケーションを開発モードで起動します。

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

2.1. 自動生成されたメトリクスの確認

Micrometer エクステンションは、HTTP サーバーリクエストの時間を自動的に計測します。

curl (またはブラウザー) を使用して、エンドポイントに数回アクセスしてみましょう:

curl http://localhost:8080/example/prime/256
curl http://localhost:8080/example/prime/7919

Micrometer Prometheus MeterRegistry エクステンションは、収集されたメトリクスの観察に使用できるエンドポイントを作成します。収集されたメトリクスを見てみましょう。

curl http://localhost:8080/q/metrics

出力で、http_server_requests_seconds_counthttp_server_requests_seconds_sumhttp_server_requests_seconds_max を探します。

リクエスト URI、HTTP メソッド (GET、POST など)、ステータスコード (200、302、404 など)、一般的な結果フィールドにディメンションラベルが追加されます。次のようなものが見つかるはずです。

# HELP http_server_requests_seconds
# TYPE http_server_requests_seconds summary
http_server_requests_seconds_count{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}"} 2.0
http_server_requests_seconds_sum{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}"} 0.017385896
# HELP http_server_requests_seconds_max
# TYPE http_server_requests_seconds_max gauge
http_server_requests_seconds_max{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}"} 0.017385896
#
メトリクスは遅延して表示され、多くの場合はアクセスされるまでエンドポイントのデータは表示されません。
エクスポートされるメトリクス形式

デフォルトでは、メトリクスはPrometheusのフォーマット application/openmetrics-text を使用してエクスポートされます。 Accept リクエストヘッダを plain/text ( curl -H "Accept: plain/text" localhost:8080/q/metrics/ ) に指定することで、以前のフォーマットに戻すことも可能です。

3. MeterRegistry の挿入

メーターを登録するには、Micrometer エクステンションが設定および維持する MeterRegistry への参照が必要です。

MeterRegistry は、次のようにアプリケーションに挿入できます:

    private final MeterRegistry registry;

    ExampleResource(MeterRegistry registry) {
        this.registry = registry;
    }

4. カウンターの追加

カウンターは、増加するだけの値を測定するために使用されます。

数値が素数かどうかをテストする頻度を追跡するカウンターを追加しましょう。このカウンター値をさまざまな方法で集計できるようにするディメンションラベル (属性またはタグとも呼ばれます) を追加します。

    @GET
    @Path("prime/{number}")
    public String checkIfPrime(@PathParam("number") long number) {
        if (number < 1) {
            registry.counter("example.prime.number", "type", "not-natural") (1)
                    .increment(); (2)
            return "Only natural numbers can be prime numbers.";
        }
        if (number == 1) {
            registry.counter("example.prime.number", "type", "one") (1)
                    .increment(); (2)
            return number + " is not prime.";
        }
        if (number == 2 || number % 2 == 0) {
            registry.counter("example.prime.number", "type", "even") (1)
                    .increment(); (2)
            return number + " is not prime.";
        }
        if (testPrimeNumber(number)) {
            registry.counter("example.prime.number", "type", "prime") (1)
                    .increment(); (2)
            return number + " is prime.";
        } else {
            registry.counter("example.prime.number", "type", "not-prime") (1)
                    .increment(); (2)
            return number + " is not prime.";
        }
    }
1 指定された値と type ラベルを持つ example.prime.number というカウンターを検索または作成します。
2 そのカウンターをインクリメントします。

4.1. 収集されたメトリクスの確認

Quarkusを開発モードで起動したままにしていない場合は、再度起動してください:

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

以下のシーケンスを試して、プレーンテキストの出力で example_prime_number_total を確認してください。

Micrometer が最初に指定されたカウンター名である example.prime.number に Prometheus 命名規則を適用すると、サフィックス _total が追加されることに注意してください。

curl http://localhost:8080/example/prime/-1
curl http://localhost:8080/example/prime/0
curl http://localhost:8080/example/prime/1
curl http://localhost:8080/example/prime/2
curl http://localhost:8080/example/prime/3
curl http://localhost:8080/example/prime/15
curl http://localhost:8080/q/metrics

example_prime_number_totaltype の値の一意の組合せごとに 1 つの測定値があることに注意してください。

このカウンターによって生成されたディメンションデータで、以下を数えることができます:

  • 負の数がチェックされた頻度: type="not-natural"

  • 1 がチェックされた頻度: type="one"

  • 偶数がチェックされた頻度: type="even"

  • 素数がチェックされた頻度: type="prime"

  • 非素数がチェックされた頻度: type="not-prime"

これらの値をすべて集計することで、(一般的に) 数値がチェックされた頻度をカウントすることもできます。

5. タイマーの追加

タイマーは、期間を測定するための特殊な抽象化です。数値が素数かどうかを判断するのにかかる時間を測定するタイマーを追加しましょう。

    @GET
    @Path("prime/{number}")
    public String checkIfPrime(@PathParam("number") long number) {
        if (number < 1) {
            registry.counter("example.prime.number", "type", "not-natural") (1)
                    .increment(); (2)
            return "Only natural numbers can be prime numbers.";
        }
        if (number == 1) {
            registry.counter("example.prime.number", "type", "one") (1)
                    .increment(); (2)
            return number + " is not prime.";
        }
        if (number == 2 || number % 2 == 0) {
            registry.counter("example.prime.number", "type", "even") (1)
                    .increment(); (2)
            return number + " is not prime.";
        }
        if (timedTestPrimeNumber(number)) { (3)
            registry.counter("example.prime.number", "type", "prime") (1)
                    .increment(); (2)
            return number + " is prime.";
        } else {
            registry.counter("example.prime.number", "type", "not-prime") (1)
                    .increment(); (2)
            return number + " is not prime.";
        }
    }

    protected boolean timedTestPrimeNumber(long number) {
        Timer.Sample sample = Timer.start(registry); (4)
        boolean result = testPrimeNumber(number); (5)
        sample.stop(registry.timer("example.prime.number.test", "prime", result + "")); (6)
        return result;
    }
1 指定された値と type ラベルを持つ example.prime.number というカウンターを検索または作成します。
2 そのカウンターをインクリメントします。
3 元の testPrimeNumber メソッドをラップするメソッドを呼び出します。
4 開始時間を追跡する Timer.Sample を作成します
5 時間を計測するメソッドを呼び出し、ブール値の結果を保存します
6 指定された ID と結果値を含む prime ラベルを使用して prime を検索または作成し、Timer.Sample によってキャプチャされた期間を記録します。

5.1. 収集されたメトリクスの確認

Quarkusを開発モードで起動したままにしていない場合は、再度起動してください:

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

Micrometer は、このタイマーのメトリクスを出力するときに Prometheus 規則を適用します。具体的には、測定された期間は秒に変換され、この単位がメトリクス名に含まれます。

以下のシーケンスを試して、プレーンテキストの出力で次のエントリーを確認してください。

  • example_prime_number_test_seconds_count — メソッドが呼び出された回数

  • example_prime_number_test_seconds_sum — すべてのメソッド呼び出しの合計時間

  • example_prime_number_test_seconds_max — 減衰間隔内での最大観測時間。メソッドが頻繁に呼び出されない場合、この値は 0 に戻ります。

curl http://localhost:8080/example/prime/256
curl http://localhost:8080/q/metrics
curl http://localhost:8080/example/prime/7919
curl http://localhost:8080/q/metrics

このカウンターによって生成されたディメンションデータを見ると、合計とカウントを使用して、数値が素数であるかどうかを判断するのにかかる (平均) 時間を計算できます。ディメンションラベルを使用すると、素数とそれ以外の数の期間に大きな違いがあるかどうかを理解できます。

6. ゲージの追加

ゲージは、車のスピードメーターのように、時間とともに増減する値を測定します。 ゲージの値は蓄積されず、収集時に観察されます。 ゲージを使用して、コレクションのサイズや関数から返される値を観察します。

    private final LinkedList<Long> list = new LinkedList<>(); (1)

    ExampleResource(MeterRegistry registry) {
        this.registry = registry;
        registry.gaugeCollectionSize("example.list.size", Tags.empty(), list); (2)
    }

    @GET
    @Path("gauge/{number}")
    public Long checkListSize(@PathParam("number") long number) { (3)
        if (number == 2 || number % 2 == 0) {
            // add even numbers to the list
            list.add(number);
        } else {
            // remove items from the list for odd numbers
            try {
                number = list.removeFirst();
            } catch (NoSuchElementException nse) {
                number = 0;
            }
        }
        return number;
    }
1 任意の数値を保持するリストを定義します。
2 リストのサイズを追跡するゲージを登録します。
3 REST エンドポイントを作成して、リストに要素を追加します。偶数はリストに追加され、奇数はリストから要素を削除します。

6.1. 収集されたメトリクスの確認

Quarkusを開発モードで起動したままにしていない場合は、再度起動してください:

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

次に、以下のシーケンスを試して、プレーンテキストの出力で example_list_size を確認してください。

curl http://localhost:8080/example/gauge/1
curl http://localhost:8080/example/gauge/2
curl http://localhost:8080/example/gauge/4
curl http://localhost:8080/q/metrics
curl http://localhost:8080/example/gauge/6
curl http://localhost:8080/example/gauge/5
curl http://localhost:8080/example/gauge/7
curl http://localhost:8080/q/metrics

まとめ

おめでとうございます!

これで Micrometer および Prometheus Meter Registry エクステンションを使用してメトリクスを収集するプロジェクトの作成が完了しました。Quarkus が自動的にキャプチャするいくつかのメトリクスを確認し、アプリケーションに固有の CounterTimer を追加しました。さらに、メトリクスにディメンションラベルを追加し、それらのラベルが prometheus エンドポイントによって出力されるデータをどのように形成するかを観測しました。

関連コンテンツ