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

Using Java Flight Recorder

This guide explains how Java Flight Recorder (JFR) can be extended to provide additional insight into your Quarkus application. JFR records various information from the Java standard API and JVM as events. By adding the Quarkus JFR extension, you can add custom Quarkus events to JFR. This will help you solve potential problems in your application.

JFR can be preconfigured to dump a file, and when the application exits, JFR will output the file. The file will contain the content of the JFR event stream to which Quarkus custom events have also been added. You can, of course, get this file at any time you want, even if your application exits unexpectedly.

前提条件

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

  • 約15分

  • IDE

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

  • Apache Maven 3.9.9

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

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

アーキテクチャ

In this guide, we create a straightforward REST application to demonstrate JFR usage.

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.17.4: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"

This command generates the Maven project and imports the Quarkus JFR extension, which includes the default JFR support.

If you already have your Quarkus project configured, you can add the JFR extension to your project by running the following command in your project base directory:

コマンドラインインタフェース
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"

With the Java Flight Recorder and the application running, we can make a request to the provided endpoint:

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

This is all that is needed to write the information to JFR.

Save the JFR events to a file

As mentioned above, the Quarkus application was configured to also start JFR at startup and dump it to a myrecording.jfr when it terminates. So we can get the file when we hit CTRL+C or type q to stop the application.

Or, we can also dump it with the jcmd command.

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

Running the jcmd command give us a list of running Java processes and the PID of each process.

JFRダンプファイルを開く

We can open a JFR dump using two tools: the jfr CLI and JDK Mission Control (JMC). It is also possible to read them using JFR APIs, but we won’t go into that in this guide.

jfr CLI

The jfr CLI is a tool included in OpenJDK. The executable file is $JAVA_HOME/bin/jfr. We can use the jfr CLI to see a list of events limited to those related to Quarkus in the dump file by running the following command:

jfr print --categories quarkus myrecording.jfr

JDK Mission Control

JMC is essentially a GUI for JFR. Some Java distributions include JMC, but if not, you need to download it manually.

To see a list of events using JMC, first we load the JFR file in JMC as follows.

jmc -open myrecording.jfr

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

To view Quarkus events in tabular style, select the Event Browser on the left side of JMC, then open the Quarkus event type tree on the right side of JMC.

JDK Mission Control Event Browser view

To see Quarkus events in chronological order on a thread, select the Java application and Threads on the left side of JMC.

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

This extension is able to records multiple JFR events concurrently (emitted by different threads or from the same thread in the case of the reactive execution model). Thus, events might overlap in JMC. This could make it difficult for you to see the events you want to see. To avoid this, we recommend to use Request IDs to filter events so that you only see the information about the requests you want to see.

イベント

リクエストの特定

This extension collaborates with the OpenTelemetry extension. The events recorded by this extension have a trace ID and a span ID. These are recorded with the OpenTelemetry IDs respectively.

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

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

For now, Quarkus only records REST events, but we plan to use this ID to link each event to each other as we add more events in the future.

イベントの実装方針

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

To solve this problem, Quarkus can also record start and end events at the beginning and end of processing. These events are disabled by default. However, we can enable these events in JFR, as described below.

REST API イベント

This event is recorded when either the Quarkus REST or RESTEasy extension is enabled. The following three JFR events are recorded as soon as REST server processing is complete.

REST

Records the time period from the start of the REST server process to the end of the REST server process.

RestStart

Records the start of the REST server process.

RestEnd

Records the end of the REST server process.

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

  • HTTPメソッド

  • URI

  • リソースクラス

  • リソースメソッド

  • クライアント

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

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

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

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

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

ネイティブ・イメージ

Native executables supports Java Flight Recorder. This extension also supports native executables.

To enable JFR on native executables, it is usually built with --enable-monitoring. However, we can enable JFR in Quarkus native executables by adding jfr to the configuration property quarkus.native.monitoring. There are two ways to set up this configuration: by including it in the application.properties or by specifying it at build time.

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

quarkus.native.monitoring=jfr

Then build your native executable as usual:

コマンドラインインタフェース
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.native.enabled=true

The second way is to pass -Dquarkus.native.monitoring=jfr at build time:

コマンドラインインタフェース
quarkus build --native -Dquarkus.native.monitoring=jfr
Maven
./mvnw install -Dnative -Dquarkus.native.monitoring=jfr
Gradle
./gradlew build -Dquarkus.native.enabled=true -Dquarkus.native.monitoring=jfr

Once you have finished building the native executable, you can run the native application with JFR as follows:

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

Note that at this time, Mandrel and GraalVM cannot record JFR for Windows native executables.

JFRの設定

We can use the JFR CLI to configure the events that JFR will record. The configuration file, JFC file, is in XML format, so we can modify it with a text editor. However, it is recommended to use jfr configure, which is included in OpenJDK by default.

Here we create a configuration file in which REST Start and REST End events are recorded (they are not recorded by default):

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

This creates custom-rest.jfc as a configuration file with recording for RestStart and RestEnd enabled.

Now we are ready to run our application with the new settings. We launch the application with JFR configured so that it is enabled from the startup of the Java Virtual Machine.

コマンドラインインタフェース
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"

Adding new events

This section is for those who would like to add new events to this extension.

We recommend that new events be associated with existing events. Associations are useful when looking at the details of a process that is taking a long time. For example, a general REST application retrieves the data needed for processing from a datastore. If REST events are not associated with datastore events, it is impossible to know which datastore events were processed in each REST request. When the two events are associated, we know immediately which datastore event was processed in each REST request.

Datastore events are not implemented yet.

The Quarkus JFR extension provides a Request ID for event association. See リクエストの特定 for more information on Request IDs.

具体的なコードとしては、以下の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
}

Then you get the information to store in the event from the IdProducer object’s getTraceId() and 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 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

関連コンテンツ