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

JDK Flight Recorder を使う

このガイドでは、https://openjdk.org/jeps/328[JDK Flight Recorder] (JFR) を拡張して、みなさんのQuarkusアプリケーションに対する洞察を提供する方法について説明します。 JFRは、Java標準APIやJVMからの様々な情報をイベントとして記録します。 この拡張機能を追加することで、みなさんは独自のQuarkusイベントをJFRに追加できます。これは、みなさんがアプリケーションの問題を解決するのに役立つでしょう。

JFRはファイルをダンプするように事前に設定でき、アプリケーションが終了すると、JFRはそのファイルを出力します。 このファイルには、Quarkusの独自イベントも追加されたJFRイベントストリームの内容が含まれます。 もちろん、アプリケーションが予期せず終了した場合であっても、あなたは必要なときにこのファイルを取得できます。

前提条件

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

  • 約15分

  • IDE

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

  • Apache Maven 3.9.7

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

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

アーキテクチャ

このガイドでは、JFRをデモするために簡単なRESTアプリケーションを作成します。

Mavenプロジェクトの作成

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

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

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

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

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

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

Windowsユーザーの場合:

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

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

このコマンドはMavenプロジェクトを生成し、標準のJFRサポートを含む quarkus-jfr エクステンションをインポートします。

もし既にQuarkusプロジェクトが存在していれば、あなたのプロジェクトのベースディレクトリで以下のコマンドを実行することで、 quarkus-jfr エクステンションをプロジェクトに追加で きます:

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

これにより、ビルドファイルに以下が追加されます:

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

Jakarta REST リソースの調査

src/main/java/org/acme/jfr/JfrResource.java ファイルを以下の内容で作成します:

package org.acme.jfr;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/hello")
public class JfrResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello";
    }
}

アプリケーションにはJFR固有のコードが含まれていないことに注目していただきたい。デフォルトでは、このエンドポイントに送られたリクエストは、コードを変更することなくJFRに記録されます。

QuarkusアプリケーションとJFRの実行

これで、アプリケーションを実行する準備ができました。 Java仮想マシンの起動時にJFRが有効になるように設定すれば、 アプリケーションをJFRと一緒に起動できます。

コマンドラインインタフェース
quarkus dev -Djvm.args="-XX:StartFlightRecording=name=quarkus,dumponexit=true,filename=myrecording.jfr"
Maven
./mvnw quarkus:dev -Djvm.args="-XX:StartFlightRecording=name=quarkus,dumponexit=true,filename=myrecording.jfr"
Gradle
./gradlew --console=plain quarkusDev -Djvm.args="-XX:StartFlightRecording=name=quarkus,dumponexit=true,filename=myrecording.jfr"

JDK Flight Recorderとアプリケーションを実行した状態で、提供されたエンドポイントにリクエストできます:

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

これだけでJFRに情報を書き込めます。

JFRをファイルに保存

前述したように、Quarkusのアプリケーションの起動時にJFRも起動し、終了時に myrecording.jfr に出力するように設定されています。 そのため、 CTRL+C を押すか、 q をタイプしてアプリケーションを停止すると、そのファイルを取得できます。

また、jcmdコマンドで出力することもできます。

jcmd <PID> JFR.dump name=quarkus filename=myrecording.jfr

jcmdコマンドを実行すると、実行中のJavaプロセスの一覧と各プロセスのPIDが表示されます。

JFRダンプファイルを開く

2つのツールを使ってJFRダンプを開くことがでます: jfr CLI と JDK Mission Control(JMC)です。 JFRのAPIを使ってダンプを読む方法もあるが、ここでは触れません。

jfr CLI

jfr CLIはOpenJDKに含まれているツールです。 その実行ファイルは $JAVA_HOME/bin/jfr です。 jfr CLIを使って、以下のようにすれば、ダンプ・ファイル内のQuarkusに関連するイベントに限定したイベントの一覧を表示できます。

jfr print --categories quarkus myrecording.jfr

JDK Mission Control

JMCは基本的にJFRのGUIビューワーです。 OpenJDKのバイナリにJMCが含まれているディストリビューションもありますが、そうでない場合は手動でダウンロードする必要があります。 JMCを使ってイベントの一覧を見るには、まずJFRファイルを以下のようにJMCに読み込ます。

jmc -open myrecording.jfr

JFRファイルを開くと、2つの選択肢があります。 ひとつは、イベントを表形式の一覧として表示する方法、もうひとつは、イベントが発生したスレッド上で時系列順に表示する方法です。

Quarkusのイベントを表形式で表示するには、JMCの左側にあるイベントブラウザを選択し、JMCの右側にあるQuarkusイベントタイプツリーを開きます。

JDK Mission Control Event Browser view

Quarkusのイベントをスレッド上で時系列順に表示するには、JMCの左側にある Java applicationThreads を選択します。

JDK Mission Control thread view

標準の設定では、Quarkusのイベントは表示されません。 Quarkusのイベントを表示するには、3つの手順を実行する必要があります。

  1. 右クリックして、 Edit Thread Activity Lanes…​ を選択します。

  2. 左側にある新しいレーンを追加する+ボタンを選択し、チェックするとそのレーンが表示されます。

  3. レーンに表示さ せたいイベントタイプとしてQuarkusを選択し、OKを押します。

JDK Mission Control Edit Thread Activity Lanes

これで、スレッドごとのQuarkusイベントを表示できるようになりました。

JDK Mission Control thread view

ノンブロッキングでは、1つのスレッドで複数の処理が見かけ上同時に処理されます。 そのため、このエクステンションでは複数の JFR イベントが同時に記録され、JMC 上でいくつものイベントが重なるかもしれません。 このため、見たいイベントを見ることが難しくなることがあるでしょう。 これを避けるには、リクエストID を使ってイベントを絞り込み、見たいリクエストの情報だけを表示させることをお勧めします。

イベント

リクエストの特定

このエクステンションは OpenTelemetry エクステンションと連携します。 このエクステンションによって記録されるイベントには、トレース ID とスパン ID を持ちます。これらはそれぞれ OpenTelemetry の ID で記録されます。

つまり、OpenTelemetry により提供される UI から、対象のトレース ID とスパン ID を特定した後、その ID を使って JFR ですぐに詳細にジャンプできます。

もし OpenTelemetry エクステンションを有効にしていない場合、このエクステンションはリクエストごとに ID を作成し、それを traceId として JFR イベントに紐付けます。 この場合、スパン ID は null になります。

今のところ、QuarkusにはRESTイベントしかありませんが、将来的にイベントを追加する際には、このIDを使用して各イベントを互いに紐付ける予定です。

イベントの実装方針

JFRがイベントを記録し始めた時点では、そのイベントはまだJFRに記録されていませんが、そのイベントをコミットした時点で、そのイベントは記録されます。 そのため、ダンプ時に記録を開始しているが、まだコミットしていないイベントはダンプされません。 これはJFRの設計上避けられません。 これは、処理が長引いた場合、イベントが永遠に記録されないことを意味します。 そのため、処理が長引いたことを知ることはできません。

この問題を解決するために、Quarkusは処理の開始時と終了時にそれぞれ開始イベントと終了イベントを記録できます。 これらのイベントはデフォルトでは無効です。 しかし、JFRでこれらのイベントを有効にできます(後述)。

REST API イベント

このイベントは quarkus-rest または resteasy-classic エクステンションが有効な場合に記録されます。 REST サーバの処理が完了すると、次の 3 つの JFR イベントが記録されます。

  • REST

  • REST Start

  • REST End

REST イベントは、REST 処理の開始から その終了までの期間を記録します。

REST Start イベントは、REST サーバ処理の開始を記録します。

REST End イベントは、REST サーバ処理の終了を記録します。

これらのイベントには以下の情報があります。

  • HTTPメソッド

  • URI

  • リソースクラス

  • リソースメソッド

  • クライアント

HTTP メソッドは、クライアントがアクセスした HTTP メソッドを記録します。これは、GET、POST、DELETE などの一般的な HTTP メソッドの文字列を記録します。

URIはクライアントがアクセスしたURIパスを記録します。これにはホスト名やポート番号は含まれません。

リソースクラスは実行されたクラスを記録します。HTTPメソッドとURIによって、リソースクラスが期待通りに実行されたかどうかを確認できます。

リソースメソッドは実行されたメソッドを記録します。 HTTP メソッドと URI によって、リソースメソッドが期待通りに実行されたかどうかを確認できます。

クライアントは、アクセスしたクライアントに関する情報を記録します。

ネイティブ・イメージ

ネイティブイメージはJDK Flight Recorderをサポートしています。 このエクステンションはネイティブイメージにも対応しています。 ネイティブイメージでJFRを有効にするには、通常、 --enable-monitoring を用いてビルドします。 しかし、 quarkus.native.monitoring の設定に jfr を追加することで、Quarkus のネイティブイメージでJFRを有効にできます。 この設定を行うには2つの方法があります。 application.properties に含める方法と、ビルド時に指定する方法です。

最初の方法は、まず application.properties で設定を行います。

application.properties

quarkus.native.monitoring=jfr

次に、 ./mvnw package -Dnative としてビルドします。

2つ目の方法は、ビルド時に -Dquarkus.native.monitoring=jfr を与え、 ./mvnw package -Dnative -Dquarkus.native.monitoring=jfr としてビルドします。

ネイティブ・イメージのビルドが完了したら、次のように JFR とともにネイティブ・アプリケーションを実行します。

target/your-application-runner -XX:StartFlightRecording=name=quarkus,dumponexit=true,filename=myrecording.jfr

現時点では、GraalVM は Windows ネイティブ・イメージ上で JFR を記録できません。

JFRの設定

JFR CLI を使用して、JFR が記録するイベントを設定できます。 その設定ファイルである JFC ファイルは XML 形式なので、テキストエディタで変更できます。 しかし、OpenJDKに標準で含まれている jfr configure を使うべきです。

ここでは、デフォルトでは記録されないRestStartとRestEndイベントが記録される設定ファイルを作成します。

jfr configure --input default.jfc +quarkus.RestStart#enabled=true +quarkus.RestEnd#enabled=true --output custom-rest.jfc

これは、RestStartとRestEndを有効にした設定ファイルとして custom-rest.jfc を作成します。

これで、新しい設定でアプリケーションを実行する準備ができました。Java 仮想マシンの起動時から JFR が有効になるように設定して、アプリケーションを起動します。

コマンドラインインタフェース
quarkus dev -Djvm.args="-XX:StartFlightRecording=name=quarkus,settings=./custom-rest.jfc,dumponexit=true,filename=myrecording.jfr"
Maven
./mvnw quarkus:dev -Djvm.args="-XX:StartFlightRecording=name=quarkus,settings=./custom-rest.jfc,dumponexit=true,filename=myrecording.jfr"
Gradle
./gradlew --console=plain quarkusDev -Djvm.args="-XX:StartFlightRecording=name=quarkus,settings=./custom-rest.jfc,dumponexit=true,filename=myrecording.jfr"

quarkus-jfrエクステンションへ新しいイベントを導入

このセクションは、このエクステンションに新しいイベントを追加したい方のためのセクションです。

新しいイベントは既存のイベントと紐付けることを推奨します。 紐付けは、時間がかかっている処理の詳細を見るときに便利です。 例えば、一般的な REST アプリケーションは、データストアから処理に必要なデータを取得します。 REST イベントがデータストア・イベントと関連付けられていない場合、各 REST リクエストでどのデータストア・イベントが処理されたかを知ることはできません。 2 つのイベントが紐付けられていれば、各 REST 要求でどのデータストア・イベントが処理されたかを即座に把握できます。

データストアのイベントはまだ実装されていません。

quarkus-jfr エクステンションは、イベントの紐付けのためにリクエスト ID を提供します。 リクエストIDの詳細については、リクエストの特定を参照してください。

具体的なコードとしては、以下の2ステップが必要です。 まず、新しいイベントに traceIdspanId を次のように実装します。 これらのイベントに付けられた @TraceIdRelational@SpanIdRelational が紐付けを行います。

import io.quarkus.jfr.runtime.SpanIdRelational;
import io.quarkus.jfr.runtime.TraceIdRelational;
import jdk.jfr.Description;
import jdk.jfr.Event;
import jdk.jfr.Label;

public class NewEvent extends Event {

    @Label("Trace ID")
    @Description("Trace ID to identify the request")
    @TraceIdRelational
    protected String traceId;

    @Label("Span ID")
    @Description("Span ID to identify the request if necessary")
    @SpanIdRelational
    protected String spanId;

    // other properties which you want to add
    // setters and getters
}

そして、 IdProducer オブジェクトの getTraceId()getSpanId() から、それらに格納する情報を取得します。

import io.quarkus.jfr.runtime.IdProducer;

public class NewInterceptor {

    @Inject
    IdProducer idProducer;

    private void recordedInvoke() {
        NewEvent event = new NewEvent();
        event.begin();

        // The process you want to measure

        event.end();
        if (endEvent.shouldCommit()) {
            event.setTraceId(idProducer.getTraceId());
            event.setSpanId(idProducer.getSpanId());
            // call other setters which you want to add
            endEvent.commit();
        }
    }
}

quarkus-jfr 設定リファレンス

ビルド時に固定される構成プロパティ - 他のすべての構成プロパティは実行時にオーバーライド可能

Configuration property

タイプ

デフォルト

If false, only quarkus-jfr events are not recorded even if JFR is enabled. In this case, Java standard API and virtual machine information will be recorded according to the setting. Default value is true

Environment variable: QUARKUS_JFR_ENABLED

Show more

ブーリアン

true

If false, only REST events in quarkus-jfr are not recorded even if JFR is enabled. In this case, other quarkus-jfr, Java standard API and virtual machine information will be recorded according to the setting. Default value is true

Environment variable: QUARKUS_JFR_REST_ENABLED

Show more

ブーリアン

true

関連コンテンツ