#chiroito ’s blog

Java を中心とした趣味の技術について

JFRのイベントをCSVに出力する

JDK Flight RecorderファイルをJDK Mission Controlで分析して異常を見つけたら、レポートを作成するためにExcelに取り込みたいなぁと思うことがあります。その場合はjfrコマンドとjqコマンドを使って任意のイベントをCSVに変換しましょう。

イベントを探す

CSVファイルを作る前に、イベントが持つ要素名を取得しないといけません。そのためにはまず、そのイベントの名前を探して、次にそのイベントが持つ要素名を取得します。

ここで使用するのはjfrコマンドのサブコマンドであるmetadataです。これらを以下のように使います。

jfr metadata <JFR file>  | grep @Name
jfr metadata --events <Event Name> <JFR file>

まず最初に、イベントの名前を探しましょう。イベントの名前はメタデータの@Nameアノテーションに記載されています。そのため、JFRファイルが持っているイベントの@Nameアノテーションを全て表示しましょう。たくさん出力されるので、grepを追加して引っかかりそうなキーワードでフィルタすることをお奨めします。

> jfr metadata <JFR file>  | grep @Name
@Name("jdk.TenuringDistribution")
@Name("jdk.ThreadAllocationStatistics")
@Name("jdk.ThreadCPULoad")
@Name("jdk.ThreadContextSwitchRate")
@Name("jdk.ThreadDump")
@Name("jdk.ThreadEnd")
@Name("jdk.ThreadPark")
@Name("jdk.ThreadSleep")
@Name("jdk.ThreadStart")
@Name("jdk.UnsignedIntFlag")
@Name("jdk.UnsignedIntFlagChanged")
@Name("jdk.UnsignedLongFlag")
@Name("jdk.UnsignedLongFlagChanged")
@Name("jdk.YoungGarbageCollection")

次に、そのイベントが持つメタデータを取得しましょう。metadataサブコマンドではJFRが持つ全てのイベントのメタデータを表示します。イベントの数は膨大にあるのでその出力結果からメタデータを探すのは大変です。そのため、先ほど取得したイベントの名前でフィルタを掛けて実行しましょう。今回はjdk.CPULoadというCPUの負荷情報のメタデータを取得します。

> jfr metadata --events jdk.CPULoad <JFR file>
@Name("jdk.CPULoad")
@Category({"Operating System", "Processor"})
@Label("CPU Load")
@Description("OS CPU Load")
class CPULoad extends jdk.jfr.Event {
  @Label("Start Time")
  @Timestamp("TICKS")
  long startTime;

  @Percentage
  @Label("JVM User")
  float jvmUser;

  @Percentage
  @Label("JVM System")
  float jvmSystem;

  @Percentage
  @Label("Machine Total")
  float machineTotal;
}

イベントをCSVへ出力

これでようやく本題であるイベントをCSVへ出力できます。以下のようなコマンドを使用してCSVへ出力します。

jfr print --json --events <イベント名> <JFR file>  | jq -r ".recording.events[].values | [<出力したい要素を羅列>] | @csv"

出力したい要素は先ほど取得したイベントのメタデータからフィールド名に該当する部分の先頭に.を付けて、各要素間はカンマ区切りで羅列します。CPULoadのJVMのユーザ領域とシステム領域を出力する場合は.jvmUser, .jvmSystemになります。

以下の例は、イベントの発生時間、JVMのユーザ領域の使用量、JVMのシステム領域の使用量、マシン全体の使用量を出力する例です。

jfr print --json --events jdk.CPULoad <JFR file>  | jq -r ".recording.events[].values | [.startTime, .jvmUser, .jvmSystem, .machineTotal] | @csv"
"2022-03-23T1:23:45.123456789+09:00",0.01234567890,0.01234567890,0.01234567
"2022-03-23T1:23:46.123456789+09:00",0.01234567890,0.01234567890,0.01234567
"2022-03-23T1:23:47.123456789+09:00",0.01234567890,0.01234567890,0.01234567

この結果をファイルにリダイレクトすることでCSVファイルが作成されます。

おまけ

JFRのイベントには期間(Duration)を持つイベントが多くあります。そのようなイベントの1つであるソケット読み込みの例も記載しておきます。メタデータの詳細が気になる場合は先述の手順で取得してください。

jfr print --json --events jdk.SocketRead <JFR file>  | jq -r ".recording.events[].values | [.startTime, .duration, .host, .address] | @csv"
"2022-03-23T1:23:45.123456789+09:00","PT0.012345678S","service-a","172.30.1.1"
"2022-03-23T1:23:45.123456789+09:00","PT0.012345678S","service-b","172.30.1.2"
"2022-03-23T1:23:45.123456789+09:00","PT0.012345678S","service-a","172.30.1.2"