Continuing from part 1 and part 2, while from JDK 14 events can be consumed on the fly, previous JDK versions (from JDK 11) offer a public API useful enough to control Flight Recorder programmatically or to read events from a JFR file.
Configuration c = Configuration.getConfiguration("profile"); [1] Recording r = new Recording(c); r.setName("monitor jvm"); r.enable("jdk.*"); [2] r.setMaxAge(java.time.Duration.ofMinutes(4)); [3] r.start(); [4] // to be profiled r.stop(); [5] r.dump(Files.createFile("/var/log/jfr/app-initiated.jfr")); [6]
- As shown above, choose the JFR configuration.
- Choose which events the recording should be interested in. Another signature accepts classes, it’s unlikely to be helpful for JDK events, but it may get interesting for custom events, your classes.
- Eventually set recording constraints, like the maximum age of the records.
- Hit record.
- When the recording session is over, stop JFR.
- Then store the results in the location of your choosing.
The above snippet creates a continuous profiling session with a 4 minute window.
Now the API allows reading emitted .jfr
files. The API represents what’s actually in a file, a schema of the events and the events themselves.
try(RecordingFile rf = new RecordingFile(Paths.get("/var/log/jfr/app-initiated.jfr"))( { [1] // read the schema rf.readEventTypes().forEach((EventType et) -> { [2] System.out.println(et.getName()); et.getFields() .stream() .map((ValueDescriptor vd) -> vd.getName()) .forEach(System.out::println); }); // actual events for(jdk.jfr.consumer.RecordedEvent e = rf.readEvent(); rf.hasMoreEvents(); e = rf.readEvent()) { [3] System.out.println(e.getEventType().getName()); [4] } }
- Open the JFR file, it’s a
Closeable
and it reads a file, so be sure to use it in atry-with-resources
block. readEventTypes()
gets you the schema of the events, fields name, labels, thresholds, etc.- Then there’s this weird enumeration style API to read the events
hasMoreEvents()
andreadEvent()
. - Access details on the event type.
RecordingFile
API is a bit awkward to work with, more specifically parsing each event requires looking at the event descriptor (via getEventType()
, or getFields()), and interrogate the event as fields presence may evolve with each JDK revision. The javadoc advises defensive programming style when reading a JFR file:
if (event.hasField("intValue")) { int intValue = event.getValue("intValue"); System.out.println("Int value: " + intValue); }
This API is now complemented by streaming live events JEP-349 in JDK 14 using an API RecordingStream
that is mix of the above, that’s out of scope for this article. But that’s yet another reason to make the effort to upgrade our JDK.
Such API facilities are useful especially when combined with other technologies like Spring Actuators. Yet when there’s available integration or when using these integrations is too late, like recording startup, the most actionable way to get recording is from the command line.
______________________________________________________________________________
Used with permission and thanks, initially written and published by Brice Dutheil in his blog.