diff --git a/instrumentation/runtime-metrics/runtime-metrics-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/RuntimeMetrics.java b/instrumentation/runtime-metrics/runtime-metrics-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/RuntimeMetrics.java index f2e893386ef6..0bdc3bdd1e01 100644 --- a/instrumentation/runtime-metrics/runtime-metrics-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/RuntimeMetrics.java +++ b/instrumentation/runtime-metrics/runtime-metrics-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/RuntimeMetrics.java @@ -32,29 +32,15 @@ public final class RuntimeMetrics implements Closeable { private final AtomicBoolean isClosed = new AtomicBoolean(); private final OpenTelemetry openTelemetry; - private final List recordedEventHandlers; - private final RecordingStream recordingStream; - private final CountDownLatch startUpLatch = new CountDownLatch(1); private final List observables = new ArrayList<>(); + private final JfrRuntimeMetrics jfrRuntimeMetrics; @SuppressWarnings("CatchingUnchecked") RuntimeMetrics( OpenTelemetry openTelemetry, Predicate featurePredicate, boolean disableJmx) { this.openTelemetry = openTelemetry; - this.recordedEventHandlers = HandlerRegistry.getHandlers(openTelemetry, featurePredicate); try { - recordingStream = new RecordingStream(); - recordedEventHandlers.forEach( - handler -> { - EventSettings eventSettings = recordingStream.enable(handler.getEventName()); - handler.getPollingDuration().ifPresent(eventSettings::withPeriod); - handler.getThreshold().ifPresent(eventSettings::withThreshold); - recordingStream.onEvent(handler.getEventName(), handler); - }); - recordingStream.onMetadata(event -> startUpLatch.countDown()); - Thread daemonRunner = new Thread(() -> recordingStream.start()); - daemonRunner.setDaemon(true); - daemonRunner.start(); + jfrRuntimeMetrics = JfrRuntimeMetrics.build(openTelemetry, featurePredicate); // Set up metrics gathered by JMX if (!disableJmx) { @@ -98,18 +84,8 @@ OpenTelemetry getOpenTelemetry() { } // Visible for testing - List getRecordedEventHandlers() { - return recordedEventHandlers; - } - - // Visible for testing - RecordingStream getRecordingStream() { - return recordingStream; - } - - // Visible for testing - CountDownLatch getStartUpLatch() { - return startUpLatch; + JfrRuntimeMetrics getJfrRuntimeMetrics() { + return jfrRuntimeMetrics; } /** Stop recording JFR events. */ @@ -119,9 +95,70 @@ public void close() { logger.log(Level.WARNING, "RuntimeMetrics is already closed"); return; } - recordingStream.close(); - recordedEventHandlers.forEach(RecordedEventHandler::close); + if (jfrRuntimeMetrics != null) { + jfrRuntimeMetrics.close(); + } JmxRuntimeMetricsUtil.closeObservers(observables); } + + static class JfrRuntimeMetrics implements Closeable { + private final List recordedEventHandlers; + private final RecordingStream recordingStream; + private final CountDownLatch startUpLatch = new CountDownLatch(1); + + private JfrRuntimeMetrics(OpenTelemetry openTelemetry, Predicate featurePredicate) { + this.recordedEventHandlers = HandlerRegistry.getHandlers(openTelemetry, featurePredicate); + recordingStream = new RecordingStream(); + recordedEventHandlers.forEach( + handler -> { + EventSettings eventSettings = recordingStream.enable(handler.getEventName()); + handler.getPollingDuration().ifPresent(eventSettings::withPeriod); + handler.getThreshold().ifPresent(eventSettings::withThreshold); + recordingStream.onEvent(handler.getEventName(), handler); + }); + recordingStream.onMetadata(event -> startUpLatch.countDown()); + Thread daemonRunner = new Thread(() -> recordingStream.start()); + daemonRunner.setDaemon(true); + daemonRunner.start(); + } + + static JfrRuntimeMetrics build( + OpenTelemetry openTelemetry, Predicate featurePredicate) { + if (!hasJfrRecordingStream()) { + return null; + } + return new JfrRuntimeMetrics(openTelemetry, featurePredicate); + } + + @Override + public void close() { + recordingStream.close(); + recordedEventHandlers.forEach(RecordedEventHandler::close); + } + + // Visible for testing + List getRecordedEventHandlers() { + return recordedEventHandlers; + } + + // Visible for testing + RecordingStream getRecordingStream() { + return recordingStream; + } + + // Visible for testing + CountDownLatch getStartUpLatch() { + return startUpLatch; + } + + private static boolean hasJfrRecordingStream() { + try { + Class.forName("jdk.jfr.consumer.RecordingStream"); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + } } diff --git a/instrumentation/runtime-metrics/runtime-metrics-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrExtension.java b/instrumentation/runtime-metrics/runtime-metrics-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrExtension.java index cc56dd582a46..62609b2a3f96 100644 --- a/instrumentation/runtime-metrics/runtime-metrics-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrExtension.java +++ b/instrumentation/runtime-metrics/runtime-metrics-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/JfrExtension.java @@ -47,7 +47,10 @@ public void beforeEach(ExtensionContext context) throws InterruptedException { RuntimeMetricsBuilder builder = RuntimeMetrics.builder(sdk); builderConsumer.accept(builder); runtimeMetrics = builder.build(); - runtimeMetrics.getStartUpLatch().await(30, TimeUnit.SECONDS); + RuntimeMetrics.JfrRuntimeMetrics jfrRuntimeMetrics = runtimeMetrics.getJfrRuntimeMetrics(); + if (jfrRuntimeMetrics != null) { + jfrRuntimeMetrics.getStartUpLatch().await(30, TimeUnit.SECONDS); + } } @Override diff --git a/instrumentation/runtime-metrics/runtime-metrics-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/RuntimeMetricsBuilderTest.java b/instrumentation/runtime-metrics/runtime-metrics-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/RuntimeMetricsBuilderTest.java index ff419d834613..0eca3a1673ca 100644 --- a/instrumentation/runtime-metrics/runtime-metrics-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/RuntimeMetricsBuilderTest.java +++ b/instrumentation/runtime-metrics/runtime-metrics-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/RuntimeMetricsBuilderTest.java @@ -66,7 +66,7 @@ void build() { var openTelemetry = OpenTelemetry.noop(); try (var jfrTelemetry = new RuntimeMetricsBuilder(openTelemetry).build()) { assertThat(jfrTelemetry.getOpenTelemetry()).isSameAs(openTelemetry); - assertThat(jfrTelemetry.getRecordedEventHandlers()) + assertThat(jfrTelemetry.getJfrRuntimeMetrics().getRecordedEventHandlers()) .hasSizeGreaterThan(0) .allSatisfy(handler -> assertThat(handler.getFeature().isDefaultEnabled()).isTrue()); } diff --git a/instrumentation/runtime-metrics/runtime-metrics-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/RuntimeMetricsTest.java b/instrumentation/runtime-metrics/runtime-metrics-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/RuntimeMetricsTest.java index c5137ed7e2a5..a81d62c82882 100644 --- a/instrumentation/runtime-metrics/runtime-metrics-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/RuntimeMetricsTest.java +++ b/instrumentation/runtime-metrics/runtime-metrics-java17/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java17/RuntimeMetricsTest.java @@ -71,7 +71,7 @@ void create_AllDisabled() { void builder() { try (var jfrTelemetry = RuntimeMetrics.builder(sdk).build()) { assertThat(jfrTelemetry.getOpenTelemetry()).isSameAs(sdk); - assertThat(jfrTelemetry.getRecordedEventHandlers()) + assertThat(jfrTelemetry.getJfrRuntimeMetrics().getRecordedEventHandlers()) .hasSizeGreaterThan(0) .allSatisfy(handler -> assertThat(handler.getFeature().isDefaultEnabled()).isTrue()); } @@ -82,7 +82,10 @@ void close() { try (RuntimeMetrics jfrTelemetry = RuntimeMetrics.builder(sdk).build()) { // Track whether RecordingStream has been closed AtomicBoolean recordingStreamClosed = new AtomicBoolean(false); - jfrTelemetry.getRecordingStream().onClose(() -> recordingStreamClosed.set(true)); + jfrTelemetry + .getJfrRuntimeMetrics() + .getRecordingStream() + .onClose(() -> recordingStreamClosed.set(true)); assertThat(reader.collectAllMetrics()).isNotEmpty();