Summary
Add the annotation jdk.jfr.Contextual
to indicate that a field of an event provides contextual information for other events happening in the same thread, during the same time.
Problem
Events may carry information relevant to events happening simultaneously in the same thread. For example, an HTTPRequest
event may contain a URL and a trace ID. If the thread stalls, perhaps because of lock contention, troubleshooting is simplified if the request URL and trace ID are associated with the jdk.JavaMonitorEnter
event.
Users can correlate these events today by manually examining other events that occur in the same thread while the HTTP request is active, but this is a cumbersome task. Tools such as JMC and the command `jfr print' can attempt to automate this process, but they lack knowledge of which fields provide contextual information.
Solution
If a field in an event is decorated with @Contextual
, the data of that field is propagated and displayed as an additional field in the lower-level jdk.JavaMonitorEnter
event. For example, an HTTPRRequest
event could look like this:
@Label("HTTP Request")
class HttpRequest extends Event {
@Label("Content-Type")
String contentType;
@Label("URL")
@Contextual
String url;
@Label("Trace ID"
@Contextual
String traceId;
}
The jfr print
command can now infer the URL and trace ID for the jdk.JavaMonitorEnter
event and display it like this:
$ jfr print --events JavaMonitorEnter recording.jfr
jdk.JavaMonitorEnter {
Context: HttpRequest.url = "https://com.example/service/login"
Context: HttpRequest.traceId = "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01"
startTime = 17:51:29.038 (2025-02-07)
duration = 50.56 ms
monitorClass = java.util.ArrayDeque
previousOwner = "Order Thread" (javaThreadId = 56209, virtual = true)
address = 0x60000232ECB0
eventThread = "Order Thread" (javaThreadId = 52613, virtual = true)
stackTrace = [
java.util.zip.ZipFile$CleanableResource.getInflater() line: 685
java.util.zip.ZipFile$ZipFileInflaterInputStream.<init>(ZipFile) line: 388
java.util.zip.ZipFile.getInputStream(ZipEntry) line: 355
java.util.jar.JarFile.getInputStream(ZipEntry) line: 833
...
]
Specification
src/jdk.jfr/share/classes/jdk/jfr/Contextual.java
+/**
+ * Event field annotation, specifies that the value carries contextual
+ * information.
+ * <p>
+ * Contextual information is data that applies to all events happening in the
+ * same thread from the beginning to the end of the event with a field annotated
+ * with {@code Contextual}.
+ * <p>
+ * For example, to trace requests or transactions in a system, a trace event can
+ * be created to provide context.
+ * {@snippet class = "Snippets" region = "ContextualTrace"}
+ * <p>
+ * To track details within an order service, an order event can be created where
+ * only the order ID provides context.
+ * {@snippet class = "Snippets" region = "ContextualOrder"}
+ * <p>
+ * If an order in the order service stalls due to lock contention, a user
+ * interface can display contextual information together with the
+ * JavaMonitorEnter event to simplify troubleshooting, for example:
+ * {@snippet lang=text :
+ * $ jfr print --events JavaMonitorEnter recording.jfr
+ * jdk.JavaMonitorEnter {
+ * Context: Trace.id = "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01"
+ * Context: Trace.name = "POST /checkout/place-order"
+ * Context: Order.id = 314159
+ * startTime = 17:51:29.038 (2025-02-07)
+ * duration = 50.56 ms
+ * monitorClass = java.util.ArrayDeque (classLoader = bootstrap)
+ * previousOwner = "Order Thread" (javaThreadId = 56209, virtual = true)
+ * address = 0x60000232ECB0
+ * eventThread = "Order Thread" (javaThreadId = 52613, virtual = true)
+ * stackTrace = [
+ * java.util.zip.ZipFile$CleanableResource.getInflater() line: 685
+ * java.util.zip.ZipFile$ZipFileInflaterInputStream.<init>(ZipFile) line: 388
+ * java.util.zip.ZipFile.getInputStream(ZipEntry) line: 355
+ * java.util.jar.JarFile.getInputStream(ZipEntry) line: 833
+ * ...
+ * ]
+ * }
+ * }
+ * <p>
+ * The difference between {@link Relational} and {@link Contextual} annotations
+ * is that {@link Relational} ties event data together to form a global data
+ * structure, similar to a foreign key in a relational database, but
+ * {@link Contextual} represents a state that applies to all events that happen
+ * at the same time, in the same thread. A field can be both contextual and
+ * relational at the same time.
+ * <p>
+ * A contextual field may incur overhead on a parser reading a recording file,
+ * since it must track active context, so it should be used sparingly and only
+ * where appropriate.
+ *
+ * @since 25
+ */
+@MetadataDefinition
+@Label("Context")
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface Contextual {
+}
jdk/jfr/snippet-files/Snippets.java
+ // @start region="ContextualTrace"
+ @Label("Trace")
+ @Name("com.example.Trace")
+ class TraceEvent extends Event {
+ @Label("ID")
+ @Contextual
+ String id;
+
+ @Label("Name")
+ @Contextual
+ String name;
+ }
+ // @end
+
+ void orderEvent() {
+ // @start region="ContextualOrder"
+ @Label("Order")
+ @Name("com.example.Order")
+ class OrderEvent extends Event {
+ @Label("Order ID")
+ @Contextual
+ long id;
+
+ @Label("Order Date")
+ @Timestamp(Timestamp.MILLISECONDS_SINCE_EPOCH)
+ long date;
+
+ @Label("Payment Method")
+ String paymentMethod;
+ }
+ // @end
+ }
+
- csr of
-
JDK-8356698 JFR: @Contextual
-
- Resolved
-