Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8356699

JFR: @Contextual

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Approved
    • Icon: P3 P3
    • 25
    • hotspot
    • None
    • jfr
    • minimal
    • Java API
    • JDK

      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
      +    }
      +

            egahlin Erik Gahlin
            egahlin Erik Gahlin
            Markus Grönlund
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: