Adding custom events

There is a simple Java API that can be used to contribute custom events to flight recordings. It is located under the package com.oracle.jrockit.jfr and is distributed as part of the JRockit JDK. It is located in the JRockit rt.jar.

Note

The com.oracle.jrockit.jfr API is under development, and not currently supported outside Oracle. Some internal Oracle products, like WebLogic Server already use it to plug into JRockit Flight Recorder.

To create a custom event using the API, first decide what kind of event is needed. You may recall from the start of this chapter that there are four main event types. Depending on what kind of event is needed, a different event class will need to be extended. There are four different ones, each corresponding to a different kind of event:

  • com.oracle.jrockit.jfr.InstantEvent
  • com.oracle.jrockit.jfr.DurationEvent
  • com.oracle.jrockit.jfr.TimedEvent
  • com.oracle.jrockit.jfr.RequestableEvent

Events can also be created dynamically, which will be discussed later.

Our next example creates a simple event that will be logged every time a hypothetical logging service is called. We choose to make this a timed event, as we want to know the duration of the logging calls. We also want to be able to set a threshold so that only the longest lasting events are logged.

Creating the event is easy. Following is the full source code:

import com.oracle.jrockit.jfr.EventDefinition;
import com.oracle.jrockit.jfr.TimedEvent;
import com.oracle.jrockit.jfr.ValueDefinition;
@EventDefinition(name = "logentry")
public class LogEvent extends TimedEvent {
@ValueDefinition(name = "message")
private String text;
public LogEvent(String text) {
this.text = text;
}
public String getText() {
return text;
}
}

To use the event from our Java application, we simply create a new event instance, and use it like this in our logging method:

public synchronized void log(String text) {
LogEvent event = new LogEvent(text);
event.begin();
// Do logging here
event.end();
event.commit();
}

Before we can use the event, however, we need to create and register an event producer:

private static Producer registerProducer() {
try {
Producer p;
p = new Producer("Log Producer (Demo)",
"A demo event producer for the demo logger.",
"http://www.example.com/logdemo");
p.addEvent(LogEvent.class);
p.register();
return p;
} catch (Exception e) {
// Add proper exception handling.
e.printStackTrace();
}
return null;
}

The Producer reference that is returned needs to be kept alive for as long as we want the producer to be available.

That is really all that is needed. However, the previous code is not very efficient. Whenever one of our events is created, a lookup is implicitly made to find the corresponding event type. The addEvent() call when registering our event with our producer actually returns an event token that, if provided to the event constructor, avoids these global lookups altogether.

We would also like the recording engine to provide stack traces and thread information for each event. Also, to be a good event producing citizen, the event should be self documenting. Consequently, we modify the event slightly as follows:

import com.oracle.jrockit.jfr.EventDefinition;
import com.oracle.jrockit.jfr.EventToken;
import com.oracle.jrockit.jfr.TimedEvent;
import com.oracle.jrockit.jfr.ValueDefinition;
@EventDefinition(path = "log/logentry", name = "Log Entry", description = "A log call in the custom logger.", stacktrace = true, thread = true)
public class LogEvent extends TimedEvent {
@ValueDefinition(name = "Message", description = "The logged message.")
private String text;
public LogEvent(EventToken eventToken, String text) {
super(eventToken);
this.text = text;
}
public String getText() {
return text;
}
}

This means we would need to save the event token when registering the producer:

static EventToken token;
static Producer producer;
static {
registerProducer();
}
static void registerProducer() {
try {
producer = new Producer("Log Producer (Demo)", "A demo event producer for the demo logger.", "http://www.example.com/logdemo");
token = producer.addEvent(LogEvent.class);
producer.register();
} catch (Exception e) {
// Add proper exception handling.
e.printStackTrace();
}
}

And then use the stored event token like this:

public synchronized void log(String text) {
LogEvent event = new LogEvent(token, text);
event.begin();
// Do logging here
event.end();
event.commit();
}

Also, if the event is guaranteed to only be used in a thread safe manner, the text attribute can be made writable, and the event instance stored and reused like this.

private LogEvent event = new LogEvent(token);
public synchronized void log(String text) {
event.reset();//clear the instance for reuse
event.setText(text);
event.begin();
// Do logging here
event.end();
event.commit();
}

The events are disabled by default. To start recording the events, remember to enable them in the template used to start the recording. It is also possible to enable the events programmatically by creating a recording with the event enabled. The following code snippet shows how to enable all events for a producer with the URI PRODUCER_URI (for our example, that would be http://www.example.com/logdemo/) by creating a recording and then enabling the events for the recording:

FlightRecorderClient fr = new FlightRecorderClient();
FlightRecordingClient rec = fr.createRecordingObject("tmp");
for (CompositeData pd : fr.getProducers()) {
if (!PRODUCER_URI.equals(pd.get("uri"))) {
continue;
}
CompositeData events[] = (CompositeData[]) pd.get("events");
for (CompositeData d : events) {
int id = (Integer) d.get("id");
rec.setEventEnabled(id, true);
rec.setStackTraceEnabled(id, true);
rec.setThreshold(id, 200);
rec.setPeriod(id, 5);
System.out.println("Enabled event " + d.get("name"));
}
}
rec.close();
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset