Creating Custom JFR Events

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. Blinkar
  • 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:

    1. Instant events
      These are events with only one time – the time the event occurred.
    2. Requestable events
      These are events with a user configurable period.
    3. Duration events
      These are events that have a start and end time (a duration).
    4. 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:

    1. 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.
    2. 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:

    1. Construct an instance of the event (or reuse a previous instance, as discussed later).
    2. Call the start method to take the start timestamp.
    3. Set the attributes you wish to set.
    4. Call the end method to take the end timestamp.
    5. Set some more attributes if necessary.
    6. 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.

11 Responses to "Creating Custom JFR Events"

  1. Matt says:

    I’d suggest including the required import statement to get the example to work:


    import com.oracle.jrockit.jfr.*;

  2. Matt says:

    Also, thanks for the useful example and post!

  3. Lewis says:

    Marcus,

    I have followed your instructions above and everything looks ok.

    How do I get at the custom events that I have generated. I have had a cursory look in the recording file and cannot see any reference to my events. I have also used the FlightRecordingLoader to iterate the events and mine do not show up.

    Any pointers to what may be wrong?

    Using JDK1.7.0_55

    Cheers

  4. Marcus says:

    Are you sure you enabled your new event types in your recording template when you kicked off the recording? 🙂

  5. Gernot says:

    Thanks for this example, this might be very helpful!

    I’m experiencing the same issue as Lewis and when kicking off the recording, i can’t find my event (which i guess should be listed as “demo” or “My Event” anywhere “Event Details for Settings” section of the flight recording starter (JMC 5.4.0).
    Where would i find it in there, if i have followed your example as exactly as possible? Should “demo/myevent” be a path besides “Java Application” “Operating System”, …?

    I am using JDK8u20. Is there anything else to watch out for when registering the event? Does it even still work under Java 8?

    Best regards!

  6. Marcus says:

    Ooops. Should perhaps have mentioned PRODUCER.register(). Fixing.

  7. Niel says:

    Here’s an earlier version of this article: http://hirt.se/blog/?p=277

  8. Ross says:

    If I use custom events in a servlet container like Tomcat, where should I create, and keep a reference to, the Producer?

  9. I maked use of this article (thanks Marcus!) and implemented Java Agent that emits JDBC events. I thought someone might want to see the complete code so here it is :https://github.com/JakubDziworski/JavaMissionControlJdbcEventPublisherAgent

  10. Vincenzo says:

    Hi nice post.

    I am interested to create an instant event that track when an exception is throw (and the exception type also). Is possible with that api?

    Regards
    Vincenzo

    Ps I have discovered that actually by default jmc tracks exception object allocation not throw event.

  11. Marcus says:

    Hi there,

    Yes, the default Error and Exception events in JFR actually tracks the creation of the objects. This is since the implementation was cheaper that way, and because of the idiomatic use of exception types – they are usually created where they are thrown. That said, exceptions are sometimes wrapped etc. There is an open bug on fixing so that we actually report exceptions actually thrown, but it will not happen in time for JDK 9.

    Kind regards,
    Marcus

Leave a Reply

Your email address will not be published.