Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions jfr-events/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ Create JFR events that can be recorded and viewed in Java Mission Control (JMC).
* Creates Open Telemetry Tracing/Span events for spans
* The thread and stacktrace will be of the thead ending the span which might be different from the thread creating the span.
* Has the fields
* Operation Name
* Operation Name (`@Contextual`)
* Trace ID
* Parent Span ID
* Span ID
* Creates Open Telemetry Tracing/Scope events for scopes
* Thread will match the thread the scope was active in and the stacktrace will be when scope was closed
* Multiple scopes might be collected for a single span
* Has the fields
* Trace ID
* Trace ID (`@Contextual`)
* Span ID
* Supports the Open Source version of JFR in Java 11.
* Might support back port to OpenJDK 8, but not tested and classes are built with JDK 11 bytecode.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.jfrevent;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jdk.jfr.Label;
import jdk.jfr.MetadataDefinition;
import jdk.jfr.Name;

/**
* Meta-annotation to support @Contextual without compiling against JDK 25.
*
* @see <a href="https://bugs.openjdk.org/browse/JDK-8356699">JDK-8356699</a>
*/
@MetadataDefinition
@Label("Context")
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Name("jdk.jfr.Contextual")
@interface Contextual {}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
+ "in scope/active on this thread.")
class ScopeEvent extends Event {

private final String traceId;
@Contextual private final String traceId;
private final String spanId;

ScopeEvent(SpanContext spanContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
@Description("Open Telemetry trace event corresponding to a span.")
class SpanEvent extends Event {

private final String operationName;
@Contextual private final String operationName;
private final String traceId;
private final String spanId;
private final String parentId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordingFile;
Expand Down Expand Up @@ -77,6 +79,15 @@ void basicSpan() throws IOException {
.extracting(e -> e.getValue("spanId"))
.isEqualTo(span.getSpanContext().getSpanId());
assertThat(events).extracting(e -> e.getValue("operationName")).isEqualTo(OPERATION_NAME);
assertThat(events)
.extracting(
e ->
e.getFields().stream()
.filter(f -> f.getName().equals("operationName"))
.map(d -> d.getAnnotation(Contextual.class))
.filter(Objects::nonNull)
.collect(Collectors.toList()))
Comment on lines +82 to +89
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test assertion only checks that the extracted list is not null, but does not validate that the @Contextual annotation is actually present on the field. The assertion should verify that the annotation is non-null, not just that the list exists. This could allow the test to pass even if the annotation is missing from the field.

Suggested change
assertThat(events)
.extracting(
e ->
e.getFields().stream()
.filter(f -> f.getName().equals("operationName"))
.map(d -> d.getAnnotation(Contextual.class))
.collect(Collectors.toList()))
assertThat(
events.get(0).getFields().stream()
.filter(f -> f.getName().equals("operationName"))
.map(d -> d.getAnnotation(Contextual.class))
.collect(Collectors.toList()))
.singleElement()

Copilot uses AI. Check for mistakes.
.isNotEmpty();
} finally {
Files.delete(output);
}
Expand Down Expand Up @@ -119,6 +130,16 @@ void basicSpanWithScope() throws IOException, InterruptedException {
.filteredOn(e -> "Span".equals(e.getEventType().getLabel()))
.extracting(e -> e.getValue("operationName"))
.isEqualTo(OPERATION_NAME);
assertThat(events)
.filteredOn(e -> "Scope".equals(e.getEventType().getLabel()))
.extracting(
e ->
e.getFields().stream()
.filter(f -> f.getName().equals("traceId"))
.map(d -> d.getAnnotation(Contextual.class))
.filter(Objects::nonNull)
.collect(Collectors.toList()))
.isNotEmpty();

} finally {
Files.delete(output);
Expand Down
Loading