Java Flight Recorder の使用
このガイドでは、 Java Flight Recorder (JFR) を拡張して、Quarkus アプリケーションに対する洞察を深める方法を説明します。 JFR は、Java 標準 API および JVM からのさまざまな情報をイベントとして記録します。 Quarkus JFR エクステンションを追加すると、JFR にカスタム Quarkus イベントを追加できます。これにより、アプリケーションの潜在的な問題を解決できます。
JFR はファイルをダンプするように事前設定することができ、アプリケーションが終了すると JFR はファイルを出力します。 ファイルには、Quarkus カスタムイベントも追加された JFR イベントストリームの内容が含まれます。 このファイルは、アプリケーションが予期せず終了した場合でも、当然ながらいつでも取得可能です。
要件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOME
が適切に設定されていること -
Apache Maven 3.9.9
-
使用したい場合は、 Quarkus CLI
-
ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること
Maven プロジェクトの作成
まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します。
Windowsユーザーの場合:
-
cmdを使用する場合、(バックスラッシュ
\
を使用せず、すべてを同じ行に書かないでください)。 -
Powershellを使用する場合は、
-D
パラメータを二重引用符で囲んでください。例:"-DprojectArtifactId=jfr-quickstart"
このコマンドは Maven プロジェクトを生成し、Quarkus JFR エクステンションをインポートします。 これには、デフォルトの JFR サポートが含まれます。
Quarkus プロジェクトがすでに設定されている場合は、プロジェクトのベースディレクトリーで以下のコマンドを実行して、 プロジェクトにJFR エクステンションを追加できます。
quarkus extension add quarkus-jfr
./mvnw quarkus:add-extension -Dextensions='quarkus-jfr'
./gradlew addExtension --extensions='quarkus-jfr'
これにより、ビルドファイルに次の内容が追加されます。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jfr</artifactId>
</dependency>
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 が有効化されるように設定されたアプリケーションを起動できます。
quarkus dev -Djvm.args="-XX:StartFlightRecording=name=quarkus,dumponexit=true,filename=myrecording.jfr"
./mvnw quarkus:dev -Djvm.args="-XX:StartFlightRecording=name=quarkus,dumponexit=true,filename=myrecording.jfr"
./gradlew --console=plain quarkusDev -Djvm.args="-XX:StartFlightRecording=name=quarkus,dumponexit=true,filename=myrecording.jfr"
Java Flight Recorder とアプリケーションが実行中の状態で、提供されたエンドポイントにリクエストを送信できます。
$ curl http://localhost:8080/hello
hello
JFR に情報を書き込むために必要なのはこれだけです。
JFR ダンプファイルを開く
JFR ダンプは、jfr
CLI と JDK Mission Control (JMC) の 2 つのツールを使用して開くことができます。
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 です。 一部の Java ディストリビューションには JMC が含まれていますが、含まれていない場合は、手動でダウンロードする必要があります。
JMC を使用してイベントのリストを表示するには、まず以下のように JMC に JFR ファイルをロードします。
jmc -open myrecording.jfr
JFR ファイルを開いた後、2 つのオプションがあります。 1 つはイベントを表形式のリストとして表示する方法です。もう 1 つは、イベントが発生したスレッドを時系列順に表示する方法です。
Quarkus イベントを表形式で表示するには、JMC の左側にある Event Browser を選択し、JMC の右側にある Quarkus イベントタイプツリーを開きます。

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

標準設定では、Quarkus イベントは表示されません。 Quarkus イベントを表示するには、3 つのタスクを実行する必要があります。
-
右クリックして
Edit Thread Activity Lanes…
を選択します。 -
プラスボタンを選択して左側に新しいレーンを追加し、そのレーンを表示するにはそのボックスにチェックマークを入れます。
-
そのレーンに表示されるイベントタイプとして Quarkus を選択し、OK を押します。

これで、スレッドごとに Quarkus イベントを表示できます。

このエクステンションは、複数の JFR イベントを同時に記録できます (異なるスレッドから発行されたもの、またはリアクティブ実行モデルの場合は同じスレッドから発行されたもの)。 したがって、JMC ではイベントが重複する可能性があります。 これにより、表示したいイベントを表示することが難しくなる可能性があります。 これを回避するには、<<identifying-requests,Request IDs> を使用してイベントをフィルタリングし、表示したいリクエストに関する情報のみを表示します。 |
イベント
リクエストの特定
このエクステンションは 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 エクステンションのいずれかが有効化されている場合に記録されます。 REST サーバーの処理が完了するとすぐに、以下の 3 つの JFR イベントが記録されます。
- REST
-
REST サーバープロセスの開始から終了までの時間を記録します。
- RestStart
-
REST サーバープロセスの開始を記録します。
- RestEnd
-
REST サーバープロセスの終了を記録します。
これらのイベントには次の情報が含まれます。
-
HTTP メソッド
-
URI
-
リソースクラス
-
リソースメソッド
-
クライアント
HTTP メソッドは、クライアントがアクセスした HTTP メソッドを記録します。 これにより、GET、POST、DELETE などの HTTP メソッドやその他の一般的な HTTP メソッドの文字列が記録されます。
URI は、クライアントがアクセスした URI パスを記録します。 これにはホスト名やポート番号は含まれません。
リソースクラスは実行されたクラスを記録します。 HTTP メソッドと URI によって、リソースクラスが期待どおりに実行されたかどうかを確認できます。
リソースメソッドは実行されたメソッドを記録します。 HTTP メソッドと URI によって、リソースメソッドが期待どおりに実行されたかどうかを確認できます。
クライアントはアクセスしているクライアントに関する情報を記録します。
ネイティブイメージ
ネイティブ実行可能ファイルは Java Flight Recorder をサポートします。 このエクステンションはネイティブ実行可能ファイルもサポートします。
ネイティブ実行可能ファイルで JFR を有効化するには、通常、--enable-monitoring
を使用してビルドします。
ただし、設定プロパティー quarkus.native.monitoring
に jfr
を追加することで、Quarkus ネイティブ実行可能ファイルで JFR を有効化できます。
この設定をセットアップするには、application.properties
に含めるか、ビルド時に指定するかの 2 つの方法があります。
最初の方法では、最初に application.properties
を設定します。
quarkus.native.monitoring=jfr
次に、通常どおりネイティブ実行可能ファイルをビルドします。
quarkus build --native
./mvnw install -Dnative
./gradlew build -Dquarkus.native.enabled=true
2 番目の方法では、ビルド時に -Dquarkus.native.monitoring=jfr
を渡します。
quarkus build --native -Dquarkus.native.monitoring=jfr
./mvnw install -Dnative -Dquarkus.native.monitoring=jfr
./gradlew build -Dquarkus.native.enabled=true -Dquarkus.native.monitoring=jfr
ネイティブ実行可能ファイルのビルドが完了したら、以下のように JFR を使用してネイティブアプリケーションを実行できます。
target/your-application-runner -XX:StartFlightRecording=name=quarkus,dumponexit=true,filename=myrecording.jfr
現時点では、Mandrel と 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"
./mvnw quarkus:dev -Djvm.args="-XX:StartFlightRecording=name=quarkus,settings=./custom-rest.jfc,dumponexit=true,filename=myrecording.jfr"
./gradlew --console=plain quarkusDev -Djvm.args="-XX:StartFlightRecording=name=quarkus,settings=./custom-rest.jfc,dumponexit=true,filename=myrecording.jfr"
新しいイベントの追加
このセクションは、このエクステンションに新しいイベントを追加したい人向けです。
新しいイベントは既存のイベントに関連付けることを推奨します。 関連付けは、長い時間がかかっているプロセスの詳細を確認するときに役立ちます。 たとえば、一般的な REST アプリケーションは、処理に必要なデータをデータストアから取得します。 REST イベントがデータストアイベントに関連付けられていない場合、各 REST リクエストでどのデータストアイベントが処理されたかを知ることはできません。 2 つのイベントが関連付けられると、各 REST リクエストでどのデータストアイベントが処理されたかがすぐにわかります。
データストアイベントはまだ実装されていません。 |
Quarkus JFR エクステンションは、イベントの関連付けのためのリクエスト ID を提供します。 リクエスト ID の詳細は、<<identifying-requests> を参照してください。
具体的なコードでは、以下の 2 つの手順が必要です。
まず、以下のように新しいイベントに traceId
と spanId
を実装します。
これらのイベントにアタッチされた @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();
}
}
}
設定リファレンス
ビルド時に固定された設定プロパティー。その他の設定プロパティーはすべてランタイム時にオーバーライド可能です。
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 Environment variable: Show more |
ブーリアン |
|
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 Environment variable: Show more |
ブーリアン |
|