第V部 記録ファイルの解析

ParseRecordingFileSample.javaの例では、記録ファイルを解析する様々な方法について説明しています。これは、複数のHelloおよびMessageイベントの記録を開始します。

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

import jdk.jfr.Event;
import jdk.jfr.EventType;
import jdk.jfr.Label;
import jdk.jfr.Name;
import jdk.jfr.Recording;
import jdk.jfr.consumer.EventStream;
import jdk.jfr.consumer.RecordingFile;

public class ParseRecordingFileSample {
    
    @Name("com.oracle.Hello")
    @Label("Hello World!")
    static class Hello extends Event {
        @Label("Greeting")
        String greeting;
    }
    
    @Name("com.oracle.Message")
    @Label("Message")
    static class Message extends Event {
        @Label("Text")
        String text;
    }

    public static void main(String... args) throws IOException {

        try (Recording r = new Recording()) {
            r.start();
            for (int i = 0; i < 3; i++) {
                Message messageEvent = new Message();
                messageEvent.begin();
                messageEvent.text = "message " + i;
                messageEvent.commit();
                
                Hello helloEvent = new Hello();
                helloEvent.begin();
                helloEvent.greeting = "hello " + i;
                helloEvent.commit();
            }
            r.stop();
            Path file = Files.createTempFile("recording", ".jfr");
            r.dump(file);
          
            try (var recordingFile = new RecordingFile(file)) {
                System.out.println("Reading events one by one");
                System.out.println("=========================");
                while (recordingFile.hasMoreEvents()) {
                    var e = recordingFile.readEvent();
                    String eventName = e.getEventType().getName();
                    System.out.println("Name: " + eventName);
                }
                System.out.println();
                System.out.println("List of registered event types");
                System.out.println("==============================");
                for (EventType eventType : recordingFile.readEventTypes())
                {
                    System.out.println(eventType.getName());
                }
            }
            System.out.println();

            System.out.println("Reading all events at once");
            System.out.println("==========================");
            
            for (var e : RecordingFile.readAllEvents(file)) {
                String eventName = e.getEventType().getName();
                System.out.println("Name: " + eventName);
            }
            System.out.println();
            
            System.out.println("Reading events one by one, printing only "
                    + "com.oracle.Message events");
            System.out.println("========================================="
                    + "=========================");
            
            try (EventStream eventStream = EventStream.openFile(file)) {
                eventStream.onEvent("com.oracle.Message", e -> {
                    System.out.println(
                        "Name: " + e.getEventType().getName());
                });
                eventStream.start();
            }
        }
    }
}

次のコマンドを使用して、ParseRecordingFileSampleを実行します:

java ParseRecordingFileSample.java

ParseRecordingFileSampleを実行する場合は、コマンド行オプション-XX:StartFlightRecordingでFlight Recorderを起動する必要はありません。Flight Recorderは、メソッドRecording.start()によって起動されます。ParseRecordingFileSampleの出力内容は次のとおりです:

Reading events one by one
=========================
Name: com.oracle.Message
Name: com.oracle.Hello
Name: com.oracle.Message
Name: com.oracle.Hello
Name: com.oracle.Message
Name: com.oracle.Hello

List of registered event types
==============================
jdk.ThreadStart
jdk.ThreadEnd
jdk.ThreadSleep
...
jdk.X509Validation
com.oracle.Message
com.oracle.Hello

Reading all events at once
==========================
Name: com.oracle.Message
Name: com.oracle.Hello
Name: com.oracle.Message
Name: com.oracle.Hello
Name: com.oracle.Message
Name: com.oracle.Hello

Reading events one by one, printing only com.oracle.Message events
==================================================================
Name: com.oracle.Message
Name: com.oracle.Message
Name: com.oracle.Message

ファイルへの記録データの書込み

ParseRecordingFileSampleでは、記録ファイルを解析するいくつかの方法を示します。ただし、まずは記録ファイルが必要であり、このサンプルでは、コマンド行では記録ファイルが作成されません。かわりに、Recording.dump(Path)が呼び出され、記録データが一時ファイルに書き込まれます:
Path file = Files.createTempFile("recording", ".jfr");
r.dump(file);
記録は開始する必要はありますが、必ずしも停止する必要はありません。

イベントを1つずつ読取り

この方法は、記録が大量にある場合やメタデータへのアクセスが必要な場合に使用します。

メソッドRecordingFile.readEvent()が記録内の次のイベントを読み取り、記録ファイル内に未読イベントがある場合は、RecordingEvent.hasMoreEvents()trueを返します。

while (recordingFile.hasMoreEvents()) {
    var e = recordingFile.readEvent();
    String eventName = e.getEventType().getName();
    System.out.println("Name: " + eventName);
}

登録済イベント・タイプのリスト

メソッドRecordingFile.readEventTypes()は、記録内のすべてのイベント・タイプのリストを返します。

すべてのイベントの同時読取り

この方法は、メモリーに収まる比較的少量の記録に使用します。

メソッドRecordingFile.readAllEvents(Path)は、記録ファイル内のすべてのイベントのリストを返します。すべてのイベントを1回の操作で読み取る方が都合のよい、小さな記録ファイルを対象としています。これは、大きな記録ファイルの読取り用ではありません。

イベント・ストリームAPIを使用した特定イベントのみの読取り

特定のイベントのみを処理する場合は、前述のように、RecordingFile.readEvent()を使用してイベントを1つずつ読み取り、イベントの名前を確認することが可能です。ただし、イベント・ストリームAPIを使用すれば、同じタイプのイベント・オブジェクトが再利用され、割当ての負荷が低減されます。

この方法では、EventStream.openFile(Path)を使用してイベント・ストリームが作成された後、EventStream.onEvent(String eventName, Consumer)が呼び出され、eventNameがイベントの名前と一致した場合に実行されるアクションが登録されます:

try (EventStream eventStream = EventStream.openFile(file)) {
    eventStream.onEvent("com.oracle.Message", e -> {
        System.out.println("Name: " +
            e.getEventType().getName());
    });
    eventStream.start();
}