結論
ローカルに作られるリポジトリからイベントを読み込みます。イベントは非同期でこのファイルに書込まれます。そのため、本当の意味でのリアルタイムにはイベントは処理されないため気を付けましょう。
JFR Event Streamingとは
Java 14 で追加された JFR Event Streaming、以下の様に書くと JVM の内部のイベントとかをストリーミングで処理してくれる素晴らしい機能です。
import jdk.jfr.Configuration; import jdk.jfr.consumer.EventStream; import jdk.jfr.consumer.RecordingStream; import java.io.IOException; import java.text.ParseException; import java.time.Duration; import java.util.concurrent.TimeUnit; public class JfrEventStreamingLocalAsync { public static void main(String... args) { try (EventStream es = new RecordingStream(Configuration.getConfiguration("default"))) { es.onEvent("jdk.JVMInformation", System.out::println); es.startAsync(); creatingEventsProcess(); es.awaitTermination(Duration.ofSeconds(2)); } catch (ParseException | IOException | InterruptedException e) { System.err.println("Couldn't start JFR Event Streaming"); } } }
この機能、ローカル実行とリモート実行ができ、それぞれでEventStream
インスタンスを作る方法が異なります。
リモート実行はEventStream.openRepository(Path)
メソッドを使用してEventStream
インスタンスを作るので「あ~JFRのリポジトリから取るんだな~。ですよね~」と思ってました。
また、ローカル実行の場合はEventStream
クラスのサブクラスであるRecordingStream
コンストラクタを使用します。このコンストラクタにどの設定でJFRを開始するかを指定してインスタンスを作ります。ローカル実行の場合はstart
する時に新たに JFR の記録を開始します。そのため、JFRが内部で持つバッファを見ていてイベントが追加された瞬間にリアルタイムにイベントが処理されるのだろうなぁと思っていました。
読む場所はどこか
「Javaからバッファを見るAPIが追加されてるだろうから見てみるか」と思ってソースを見たところ、EventDirectoryStream
となっていました。これはリポジトリからイベントを読み込むクラスです。このクラスのコンストラクタは第2引数にリポジトリのパスを指定します。しかし、この引数はnull
が渡されていました。
public RecordingStream() { Utils.checkAccessFlightRecorder(); AccessControlContext acc = AccessController.getContext(); this.recording = new Recording(); try { PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording); this.directoryStream = new EventDirectoryStream(acc, null, SecuritySupport.PRIVILEGED, pr); } catch (IOException ioe) { this.recording.close(); throw new IllegalStateException(ioe.getMessage()); } }
「バッファから読む場合は null なのかな?」と思って読み進めてみると、パスを決めるために実行されるRepositoryFiles
クラスのprivate boolean updatePaths() throws IOException
メソッドに以下の様なコメントが記載されていました。
// Always get the latest repository if 'jcmd JFR.configure // repositorypath=...' has been executed SafePath sf = Repository.getRepository().getRepositoryPath();
どうやら JFR の内部 API である Repository
クラスを使用して最新のリポジトリを取得しているようです。このクラスはシングルトンで、名前の通り JFR のリポジトリを管理しています。
まとめ
これまでのソースコードで、JFR Event Streaming をローカル実行すると、その JVM にある最新のリポジトリからイベントを読み込んでいるのが分かります。JVM によって記録されるイベントは非同期でこのファイルへ書込み、JFR Event Streaming は書込まれたイベントを都度読み込んで処理されます。そのため、イベントの処理はリアルタイムではなく少し遅れて処理されます。