This blog post is the first in a series of posts on using unsupported functionality in the Oracle JDK and/or Java Mission Control.
Since the 7u40 version of the Oracle JDK, the Java Flight Recorder provides a wealth of information about the operating system, the JVM and the Java application running in the JVM. To make the wealth of information easier to digest, it can often be quite useful to provide some contextual information to the events available from the Flight Recorder. For instance, WebLogic Diagnostics Framework integrates with the Flight Recorder to add information about requests, and there are trial projects that add information about JUnit tests run, JavaFX pulse logger information and more. Having that contextual information makes it much easier to get a good starting point for where you want to study the recording in more detail, for instance homing in on the longest servlet requests taking the longest time to complete.
This blog will show how to use the Flight Recorder Java API to record your own data into the Java Flight Recorder.
Disclaimer:
The following blog entry will describe UNSUPPORTED functionality. This means that relying on the described APIs or functionality may BREAK your code/plugin with any given update of the JDK and/or Mission Control.
I strongly recommend against using these APIs in production code. There will be supported APIs for recording custom events into the Java Flight Recorder eventually and they will likely be quite different. Also note that code using these APIs will require an Oracle JDK >= 7u4, both at compile and run-time. And finally, note that the entire API for recording your own events entered the Oracle JDK deprecated. This is NOT supported, and WILL CHANGE!
That said, I still think it is valuable to share this information since:
- Sometimes having this ability can mean the difference between solving a problem in a reasonable time and not.
- This is an API for recording information. If proper care is taken, you can insulate yourself from the effects of not having the API available at runtime. This will never be the kind of API that is critical to the execution of your application. That said, the API and having access to the flight recorder infrastructure can be pretty addictive if you’re interested in production time profiling and diagnostics.
- There are plenty of opportunities where you can use this API to a great advantage without even touching your application code. For example, having an event type for unit tests or maybe coding your own BCI agent to insert the code required to emit events on the fly.
- Getting some early feedback on these APIs would be quite helpful.
Different Kind of Events
Before we start recording events, we must discuss the different kind of event types available. There are currently four different ones:
- Instant events
These are events with only one time – the time the event occurred. - Requestable events
These are events with a user configurable period. - Duration events
These are events that have a start and end time (a duration). - Timed events
These are duration events with a user configurable threshold. If an event has a duration shorter than the defined threshold, it will not be recorded.
To record events we need to define two things:
- A producer
The producer is basically some metadata and a namespace for your events. In the Java API, you will register your event types with the producer. Once the producer is no longer referenced, your associated event types will be cleared. Do NOT lose the reference to your producer. - One or more event types
In event types you define the metadata used to describe the events as well as the attributes (data) available.
Defining the Producer
To define a producer, simply instantiate a Producer instance:
Producer myProducer = new Producer("Demo Producer", "A demo event producer.", http://www.example.com/demo/);
The producer is simply defined using a name – the name is what will be shown to the user in the JMC GUI, a description and an identifier. The identifier is on an URI format.
Next we must register the producer:
myProducer.register();
After making sure to keep a reference to the producer, we can start defining the event types.
Defining an Event Type
Event types are defined using a Java class, with annotations used to declare metadata. Step one is to subclass the class appropriate for the kind of event you want, for example a timed event. Next you need to decide what attributes (data) should be available in the events. The following example declares an event type named My Event, with a single attribute named Message:
@EventDefinition(path = "demo/myevent", name = "My Event", description = "An event triggered by doStuff.", stacktrace = true, thread = true)
private class MyEvent extends TimedEvent {
@ValueDefinition(name = "Message", description = "The logged important stuff.")
private String text;
public MyEvent(EventToken eventToken) {
super(eventToken);
}
public void setText(String text) {
this.text = text;
}
}
Registering the Event Type
Next you have to register the event type with the producer. The registration method returns something called an event token. Keeping a reference to the event token and using it as an argument to the construction of an event instance is much more efficient than using the default event constructor. Here is how you may go about registering your event:
EventToken myToken = myProducer.addEvent(MyEvent.class);
Emitting Events
Now we’re ready to start emitting our event. This is very easily done:
- Construct an instance of the event (or reuse a previous instance, as discussed later).
- Call the start method to take the start timestamp.
- Set the attributes you wish to set.
- Call the end method to take the end timestamp.
- Set some more attributes if necessary.
- Call the commit method to commit the event to the recording engine.
So this is how it would look in code:
public void doStuff() {
MyEvent event = new MyEvent(myToken);
event.begin();
String importantResultInStuff = "";
// Generate the string, then set it...
event.setText(importantResultInStuff);
event.end();
event.commit();
}
Now, you may come to the conclusion that this will cause an object allocation for each event. If you would rather not allocate a new object for each event, you can reuse one of them, like this:
private MyEvent event = new MyEvent(myToken);
public void doStuffReuse() {
event.reset();
event.begin();
String importantResultInStuff = "";
// Generate the string, then set it...
event.setText(importantResultInStuff);
event.end();
event.commit();
}
(The only difference is that you need to remember to reset the event between each invocation.)
Conclusion
There is a Java API for adding events to the Java Flight Recorder available in the Oracle JDK since 7u4. It is NOT SUPPORTED, and it will change. That said, it can be quite useful for providing contextual information to the rest of the data provided.
I am very interested in feedback on this API. Such feedback will be fed back into a future, officially supported, API.