8 Custom Annotations

Creating custom annotations for events is the same as creating Java annotations. The example CustomAnnotationSample.java demonstrates this.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import jdk.jfr.Description;
import jdk.jfr.Event;
import jdk.jfr.Label;
import jdk.jfr.MetadataDefinition;
import jdk.jfr.Name;
import jdk.jfr.Relational;

public class CustomAnnotationSample {
    
    @MetadataDefinition
    @Name("com.oracle.Severity")
    @Label("Severity")
    @Description("Value between 0 and 100 that indicates " +
        "severity. 100 is most severe.")
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.TYPE })
    public @interface Severity {
       int value() default 50;
    }

    @MetadataDefinition
    @Name("com.oracle.TransactionId")
    @Label("Transaction ID")
    @Relational
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.FIELD })
    public @interface TransactionId { }

    @Name("com.oracle.TransactionBlocked")
    @Severity(80)
    @Label("Transaction Blocked")
    static class TransactionBlocked extends Event {
        @TransactionId
        @Label("Transaction")
        long transaction;

        @TransactionId
        @Label("Transaction Blocker")
        long transactionBlocker;
    }

    public static void main(String... args) {
        TransactionBlocked event = new TransactionBlocked();
        event.begin();
        event.transaction = 1;
        event.transactionBlocker = 2;
        event.commit();
    }
}

Run CustomAnnotationSample with the following command:

java -XX:StartFlightRecording:filename=ca.jfr CustomAnnotationSample.java

To view annotations, categories, field layouts, and other information about all the events in customannotationsample.jfr, run the following command:

jfr metadata ca.jfr

The output of the previous command includes the following:

@Name("com.oracle.Severity")
@Label("Severity")
@Description("Value between 0 and 100 that indicates severity. 100 is most severe.")
class Severity extends java.lang.annotation.Annotation {
  int value;
}

@Name("com.oracle.TransactionId")
@Label("Transaction ID")
@Relational
class TransactionId extends java.lang.annotation.Annotation {
}
...
@Name("com.oracle.TransactionBlocked")
@Severity(80)
@Label("Transaction Blocked")
class TransactionBlocked extends jdk.jfr.Event {
  @Label("Start Time")
  @Timestamp("TICKS")
  long startTime;

  @Label("Duration")
  @Timespan("TICKS")
  long duration;

  @Label("Event Thread")
  @Description("Thread in which event was committed in")
  Thread eventThread;

  @Label("Stack Trace")
  @Description("Stack Trace starting from the method the event was committed in")
  StackTrace stackTrace;

  @TransactionId
  @Label("Transaction")
  long transaction;

  @TransactionId
  @Label("Transaction Blocker")
  long transactionBlocker;
}

To access values of custom annotations, use the EventType.getAnnotation method, which takes one argument, the Class object that corresponds to the annotation type. For example, the following code prints the events whose severity is greater than 50:

for (var e : RecordingFile.readAllEvents(file)) { 
    EventType t = e.getEventType();
    Severity s = t.getAnnotation(Severity.class);
    if (s != null && s.getValue() > 50) {
        System.out.println(e); 
    }
}

See Declaring an Annotation Type in The Java Tutorials.