counts = new HashMap<>();
- long[] threadIds = threadBean.getAllThreadIds();
- for (ThreadInfo threadInfo : threadBean.getThreadInfo(threadIds)) {
- if (threadInfo == null) {
- continue;
- }
- Attributes threadAttributes = threadAttributes(threadInfo);
- counts.compute(threadAttributes, (k, value) -> value == null ? 1 : value + 1);
- }
- counts.forEach((threadAttributes, count) -> measurement.record(count, threadAttributes));
- };
- }
-
- private static Attributes threadAttributes(ThreadInfo threadInfo) {
- boolean isDaemon;
- try {
- isDaemon = (boolean) requireNonNull(THREAD_INFO_IS_DAEMON).invoke(threadInfo);
- } catch (Throwable e) {
- throw new IllegalStateException("Unexpected error happened during ThreadInfo#isDaemon()", e);
- }
- String threadState = threadInfo.getThreadState().name().toLowerCase(Locale.ROOT);
- return Attributes.of(
- JvmAttributes.JVM_THREAD_DAEMON, isDaemon, JvmAttributes.JVM_THREAD_STATE, threadState);
- }
-
- private static Attributes threadAttributes(Thread thread) {
- boolean isDaemon = thread.isDaemon();
- String threadState = thread.getState().name().toLowerCase(Locale.ROOT);
- return Attributes.of(
- JvmAttributes.JVM_THREAD_DAEMON, isDaemon, JvmAttributes.JVM_THREAD_STATE, threadState);
+ return io.opentelemetry.instrumentation.runtimemetrics.java8.internal.Threads.registerObservers(
+ openTelemetry);
}
private Threads() {}
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/Classes.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/Classes.java
new file mode 100644
index 000000000000..afe74d074a0d
--- /dev/null
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/Classes.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.runtimemetrics.java8.internal;
+
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.metrics.Meter;
+import java.lang.management.ClassLoadingMXBean;
+import java.lang.management.ManagementFactory;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Registers measurements that generate metrics about JVM classes. The metrics generated by this
+ * class follow the
+ * stable JVM metrics semantic conventions.
+ *
+ * This class is internal and is hence not for public use. Its APIs are unstable and can change
+ * at any time.
+ */
+public class Classes {
+
+ // Visible for testing
+ static final Classes INSTANCE = new Classes();
+
+ /** Register observers for java runtime class metrics. */
+ public static List registerObservers(OpenTelemetry openTelemetry) {
+ return INSTANCE.registerObservers(openTelemetry, ManagementFactory.getClassLoadingMXBean());
+ }
+
+ // Visible for testing
+ List registerObservers(OpenTelemetry openTelemetry, ClassLoadingMXBean classBean) {
+ Meter meter = JmxRuntimeMetricsUtil.getMeter(openTelemetry);
+ List observables = new ArrayList<>();
+
+ observables.add(
+ meter
+ .counterBuilder("jvm.class.loaded")
+ .setDescription("Number of classes loaded since JVM start.")
+ .setUnit("{class}")
+ .buildWithCallback(
+ observableMeasurement ->
+ observableMeasurement.record(classBean.getTotalLoadedClassCount())));
+ observables.add(
+ meter
+ .counterBuilder("jvm.class.unloaded")
+ .setDescription("Number of classes unloaded since JVM start.")
+ .setUnit("{class}")
+ .buildWithCallback(
+ observableMeasurement ->
+ observableMeasurement.record(classBean.getUnloadedClassCount())));
+ observables.add(
+ meter
+ .upDownCounterBuilder("jvm.class.count")
+ .setDescription("Number of classes currently loaded.")
+ .setUnit("{class}")
+ .buildWithCallback(
+ observableMeasurement ->
+ observableMeasurement.record(classBean.getLoadedClassCount())));
+
+ return observables;
+ }
+
+ protected Classes() {}
+}
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/Cpu.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/Cpu.java
new file mode 100644
index 000000000000..6fe22632caac
--- /dev/null
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/Cpu.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.runtimemetrics.java8.internal;
+
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.metrics.Meter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.function.IntSupplier;
+import java.util.function.Supplier;
+import javax.annotation.Nullable;
+
+/**
+ * Registers measurements that generate metrics about CPU. The metrics generated by this class
+ * follow the
+ * stable JVM metrics semantic conventions.
+ *
+ * This class is internal and is hence not for public use. Its APIs are unstable and can change
+ * at any time.
+ */
+public class Cpu {
+
+ // Visible for testing
+ static final Cpu INSTANCE = new Cpu();
+
+ private static final double NANOS_PER_S = TimeUnit.SECONDS.toNanos(1);
+
+ /** Register observers for java runtime CPU metrics. */
+ public static List registerObservers(OpenTelemetry openTelemetry) {
+ return INSTANCE.registerObservers(
+ openTelemetry,
+ Runtime.getRuntime()::availableProcessors,
+ CpuMethods.processCpuTime(),
+ CpuMethods.processCpuUtilization());
+ }
+
+ // Visible for testing
+ List registerObservers(
+ OpenTelemetry openTelemetry,
+ IntSupplier availableProcessors,
+ @Nullable Supplier processCpuTime,
+ @Nullable Supplier processCpuUtilization) {
+ Meter meter = JmxRuntimeMetricsUtil.getMeter(openTelemetry);
+ List observables = new ArrayList<>();
+
+ if (processCpuTime != null) {
+ observables.add(
+ meter
+ .counterBuilder("jvm.cpu.time")
+ .ofDoubles()
+ .setDescription("CPU time used by the process as reported by the JVM.")
+ .setUnit("s")
+ .buildWithCallback(
+ observableMeasurement -> {
+ Long cpuTimeNanos = processCpuTime.get();
+ if (cpuTimeNanos != null && cpuTimeNanos >= 0) {
+ observableMeasurement.record(cpuTimeNanos / NANOS_PER_S);
+ }
+ }));
+ }
+ observables.add(
+ meter
+ .upDownCounterBuilder("jvm.cpu.count")
+ .setDescription("Number of processors available to the Java virtual machine.")
+ .setUnit("{cpu}")
+ .buildWithCallback(
+ observableMeasurement ->
+ observableMeasurement.record(availableProcessors.getAsInt())));
+ if (processCpuUtilization != null) {
+ observables.add(
+ meter
+ .gaugeBuilder("jvm.cpu.recent_utilization")
+ .setDescription("Recent CPU utilization for the process as reported by the JVM.")
+ .setUnit("1")
+ .buildWithCallback(
+ observableMeasurement -> {
+ Double cpuUsage = processCpuUtilization.get();
+ if (cpuUsage != null && cpuUsage >= 0) {
+ observableMeasurement.record(cpuUsage);
+ }
+ }));
+ }
+
+ return observables;
+ }
+
+ protected Cpu() {}
+}
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalBufferPools.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalBufferPools.java
index 94fb5a3546c7..81c6e61dd6de 100644
--- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalBufferPools.java
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalBufferPools.java
@@ -24,7 +24,11 @@
*
* This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
+ *
+ * @deprecated Use {@link io.opentelemetry.instrumentation.runtimemetrics.java8.RuntimeMetrics}
+ * instead, and configure metric views to select specific metrics.
*/
+@Deprecated
public final class ExperimentalBufferPools {
private static final AttributeKey JVM_BUFFER_POOL_NAME =
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalCpu.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalCpu.java
index 5fd928495687..5da495070c39 100644
--- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalCpu.java
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalCpu.java
@@ -19,7 +19,11 @@
*
* This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
+ *
+ * @deprecated Use {@link io.opentelemetry.instrumentation.runtimemetrics.java8.RuntimeMetrics}
+ * instead, and configure metric views to select specific metrics.
*/
+@Deprecated
public final class ExperimentalCpu {
/** Register observers for java runtime experimental CPU metrics. */
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalFileDescriptor.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalFileDescriptor.java
index 72e9577f7abe..ea61396f811a 100644
--- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalFileDescriptor.java
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalFileDescriptor.java
@@ -15,7 +15,11 @@
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
+ *
+ * @deprecated Use {@link io.opentelemetry.instrumentation.runtimemetrics.java8.RuntimeMetrics}
+ * instead, and configure metric views to select specific metrics.
*/
+@Deprecated
public final class ExperimentalFileDescriptor {
private static final Class> unixOperatingSystemMxBeanClass =
loadClass("com.sun.management.UnixOperatingSystemMXBean");
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalMemoryPools.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalMemoryPools.java
index 979008c04e32..186b4e839a49 100644
--- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalMemoryPools.java
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalMemoryPools.java
@@ -25,7 +25,11 @@
*
*
This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
+ *
+ * @deprecated Use {@link io.opentelemetry.instrumentation.runtimemetrics.java8.RuntimeMetrics}
+ * instead, and configure metric views to select specific metrics.
*/
+@Deprecated
public final class ExperimentalMemoryPools {
/** Register observers for java runtime experimental memory metrics. */
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/GarbageCollector.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/GarbageCollector.java
new file mode 100644
index 000000000000..2de5e0910fcc
--- /dev/null
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/GarbageCollector.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.runtimemetrics.java8.internal;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.unmodifiableList;
+
+import com.sun.management.GarbageCollectionNotificationInfo;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.common.AttributesBuilder;
+import io.opentelemetry.api.metrics.DoubleHistogram;
+import io.opentelemetry.api.metrics.Meter;
+import io.opentelemetry.semconv.JvmAttributes;
+import java.lang.management.GarbageCollectorMXBean;
+import java.lang.management.ManagementFactory;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.logging.Logger;
+import javax.management.Notification;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.openmbean.CompositeData;
+
+/**
+ * Registers instruments that generate metrics about JVM garbage collection. The metrics generated
+ * by this class follow the
+ * stable JVM metrics semantic conventions.
+ *
+ *
This class is internal and is hence not for public use. Its APIs are unstable and can change
+ * at any time.
+ */
+public class GarbageCollector {
+
+ private static final Logger logger = Logger.getLogger(GarbageCollector.class.getName());
+
+ private static final double MILLIS_PER_S = TimeUnit.SECONDS.toMillis(1);
+
+ public static final List GC_DURATION_BUCKETS =
+ unmodifiableList(asList(0.01, 0.1, 1., 10.));
+
+ private static final AttributeKey JVM_GC_CAUSE = AttributeKey.stringKey("jvm.gc.cause");
+
+ private static final NotificationFilter GC_FILTER =
+ notification ->
+ notification
+ .getType()
+ .equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION);
+
+ /** Register observers for java runtime memory metrics. */
+ public static List registerObservers(
+ OpenTelemetry openTelemetry, boolean captureGcCause) {
+ if (!isNotificationClassPresent()) {
+ logger.fine(
+ "The com.sun.management.GarbageCollectionNotificationInfo class is not available;"
+ + " GC metrics will not be reported.");
+ return Collections.emptyList();
+ }
+
+ return registerObservers(
+ openTelemetry,
+ ManagementFactory.getGarbageCollectorMXBeans(),
+ GarbageCollector::extractNotificationInfo,
+ captureGcCause);
+ }
+
+ // Visible for testing
+ public static List registerObservers(
+ OpenTelemetry openTelemetry,
+ List gcBeans,
+ Function notificationInfoExtractor,
+ boolean captureGcCause) {
+ Meter meter = JmxRuntimeMetricsUtil.getMeter(openTelemetry);
+
+ DoubleHistogram gcDuration =
+ meter
+ .histogramBuilder("jvm.gc.duration")
+ .setDescription("Duration of JVM garbage collection actions.")
+ .setUnit("s")
+ .setExplicitBucketBoundariesAdvice(GC_DURATION_BUCKETS)
+ .build();
+
+ List result = new ArrayList<>();
+ for (GarbageCollectorMXBean gcBean : gcBeans) {
+ if (!(gcBean instanceof NotificationEmitter)) {
+ continue;
+ }
+ NotificationEmitter notificationEmitter = (NotificationEmitter) gcBean;
+ GcNotificationListener listener =
+ new GcNotificationListener(gcDuration, notificationInfoExtractor, captureGcCause);
+ notificationEmitter.addNotificationListener(listener, GC_FILTER, null);
+ result.add(() -> notificationEmitter.removeNotificationListener(listener));
+ }
+ return result;
+ }
+
+ private static final class GcNotificationListener implements NotificationListener {
+
+ private final boolean captureGcCause;
+ private final DoubleHistogram gcDuration;
+ private final Function
+ notificationInfoExtractor;
+
+ private GcNotificationListener(
+ DoubleHistogram gcDuration,
+ Function notificationInfoExtractor,
+ boolean captureGcCause) {
+ this.captureGcCause = captureGcCause;
+ this.gcDuration = gcDuration;
+ this.notificationInfoExtractor = notificationInfoExtractor;
+ }
+
+ @Override
+ public void handleNotification(Notification notification, Object unused) {
+ GarbageCollectionNotificationInfo notificationInfo =
+ notificationInfoExtractor.apply(notification);
+
+ String gcName = notificationInfo.getGcName();
+ String gcAction = notificationInfo.getGcAction();
+ double duration = notificationInfo.getGcInfo().getDuration() / MILLIS_PER_S;
+ AttributesBuilder builder = Attributes.builder();
+ builder.put(JvmAttributes.JVM_GC_NAME, gcName);
+ builder.put(JvmAttributes.JVM_GC_ACTION, gcAction);
+ if (captureGcCause) {
+ String gcCause = notificationInfo.getGcCause();
+ builder.put(JVM_GC_CAUSE, gcCause);
+ }
+ gcDuration.record(duration, builder.build());
+ }
+ }
+
+ /**
+ * Extract {@link GarbageCollectionNotificationInfo} from the {@link Notification}.
+ *
+ * Note: this exists as a separate function so that the behavior can be overridden with mocks
+ * in tests. It's very challenging to create a mock {@link CompositeData} that can be parsed by
+ * {@link GarbageCollectionNotificationInfo#from(CompositeData)}.
+ */
+ private static GarbageCollectionNotificationInfo extractNotificationInfo(
+ Notification notification) {
+ return GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData());
+ }
+
+ private static boolean isNotificationClassPresent() {
+ try {
+ Class.forName(
+ "com.sun.management.GarbageCollectionNotificationInfo",
+ false,
+ GarbageCollectorMXBean.class.getClassLoader());
+ return true;
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
+ }
+
+ protected GarbageCollector() {}
+}
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/JmxRuntimeMetricsFactory.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/JmxRuntimeMetricsFactory.java
index aac919c22aec..de9464b4f8f3 100644
--- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/JmxRuntimeMetricsFactory.java
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/JmxRuntimeMetricsFactory.java
@@ -6,11 +6,6 @@
package io.opentelemetry.instrumentation.runtimemetrics.java8.internal;
import io.opentelemetry.api.OpenTelemetry;
-import io.opentelemetry.instrumentation.runtimemetrics.java8.Classes;
-import io.opentelemetry.instrumentation.runtimemetrics.java8.Cpu;
-import io.opentelemetry.instrumentation.runtimemetrics.java8.GarbageCollector;
-import io.opentelemetry.instrumentation.runtimemetrics.java8.MemoryPools;
-import io.opentelemetry.instrumentation.runtimemetrics.java8.Threads;
import java.util.ArrayList;
import java.util.List;
@@ -19,7 +14,7 @@
* any time.
*/
public class JmxRuntimeMetricsFactory {
- @SuppressWarnings("CatchingUnchecked")
+ @SuppressWarnings({"CatchingUnchecked", "deprecation"}) // ExperimentalXxx classes are deprecated
public static List buildObservables(
OpenTelemetry openTelemetry, boolean emitExperimentalTelemetry, boolean captureGcCause) {
// Set up metrics gathered by JMX
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/MemoryPools.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/MemoryPools.java
new file mode 100644
index 000000000000..9e67f014de5e
--- /dev/null
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/MemoryPools.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.runtimemetrics.java8.internal;
+
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.metrics.Meter;
+import io.opentelemetry.api.metrics.ObservableLongMeasurement;
+import io.opentelemetry.semconv.JvmAttributes;
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryPoolMXBean;
+import java.lang.management.MemoryType;
+import java.lang.management.MemoryUsage;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Registers measurements that generate metrics about JVM memory pools. The metrics generated by
+ * this class follow the
+ * stable JVM metrics semantic conventions.
+ *
+ * This class is internal and is hence not for public use. Its APIs are unstable and can change
+ * at any time.
+ */
+public class MemoryPools {
+
+ /** Register observers for java runtime memory metrics. */
+ public static List registerObservers(OpenTelemetry openTelemetry) {
+ return registerObservers(openTelemetry, ManagementFactory.getMemoryPoolMXBeans());
+ }
+
+ // Visible for testing
+ public static List registerObservers(
+ OpenTelemetry openTelemetry, List poolBeans) {
+ Meter meter = JmxRuntimeMetricsUtil.getMeter(openTelemetry);
+ List observables = new ArrayList<>();
+
+ observables.add(
+ meter
+ .upDownCounterBuilder("jvm.memory.used")
+ .setDescription("Measure of memory used.")
+ .setUnit("By")
+ .buildWithCallback(
+ callback(
+ JvmAttributes.JVM_MEMORY_POOL_NAME,
+ JvmAttributes.JVM_MEMORY_TYPE,
+ poolBeans,
+ MemoryPoolMXBean::getUsage,
+ MemoryUsage::getUsed)));
+ observables.add(
+ meter
+ .upDownCounterBuilder("jvm.memory.committed")
+ .setDescription("Measure of memory committed.")
+ .setUnit("By")
+ .buildWithCallback(
+ callback(
+ JvmAttributes.JVM_MEMORY_POOL_NAME,
+ JvmAttributes.JVM_MEMORY_TYPE,
+ poolBeans,
+ MemoryPoolMXBean::getUsage,
+ MemoryUsage::getCommitted)));
+ observables.add(
+ meter
+ .upDownCounterBuilder("jvm.memory.limit")
+ .setDescription("Measure of max obtainable memory.")
+ .setUnit("By")
+ .buildWithCallback(
+ callback(
+ JvmAttributes.JVM_MEMORY_POOL_NAME,
+ JvmAttributes.JVM_MEMORY_TYPE,
+ poolBeans,
+ MemoryPoolMXBean::getUsage,
+ MemoryUsage::getMax)));
+ observables.add(
+ meter
+ .upDownCounterBuilder("jvm.memory.used_after_last_gc")
+ .setDescription(
+ "Measure of memory used, as measured after the most recent garbage collection event on this pool.")
+ .setUnit("By")
+ .buildWithCallback(
+ callback(
+ JvmAttributes.JVM_MEMORY_POOL_NAME,
+ JvmAttributes.JVM_MEMORY_TYPE,
+ poolBeans,
+ MemoryPoolMXBean::getCollectionUsage,
+ MemoryUsage::getUsed)));
+
+ return observables;
+ }
+
+ // Visible for testing
+ public static Consumer callback(
+ AttributeKey poolNameKey,
+ AttributeKey memoryTypeKey,
+ List poolBeans,
+ Function memoryUsageExtractor,
+ Function valueExtractor) {
+ List attributeSets = new ArrayList<>(poolBeans.size());
+ for (MemoryPoolMXBean pool : poolBeans) {
+ attributeSets.add(
+ Attributes.builder()
+ .put(poolNameKey, pool.getName())
+ .put(memoryTypeKey, memoryType(pool.getType()))
+ .build());
+ }
+
+ return measurement -> {
+ for (int i = 0; i < poolBeans.size(); i++) {
+ Attributes attributes = attributeSets.get(i);
+ MemoryUsage memoryUsage = memoryUsageExtractor.apply(poolBeans.get(i));
+ if (memoryUsage == null) {
+ // JVM may return null in special cases for MemoryPoolMXBean.getUsage() and
+ // MemoryPoolMXBean.getCollectionUsage()
+ continue;
+ }
+ long value = valueExtractor.apply(memoryUsage);
+ if (value != -1) {
+ measurement.record(value, attributes);
+ }
+ }
+ };
+ }
+
+ private static String memoryType(MemoryType memoryType) {
+ switch (memoryType) {
+ case HEAP:
+ return JvmAttributes.JvmMemoryTypeValues.HEAP;
+ case NON_HEAP:
+ return JvmAttributes.JvmMemoryTypeValues.NON_HEAP;
+ }
+ return "unknown";
+ }
+
+ protected MemoryPools() {}
+}
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/Threads.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/Threads.java
new file mode 100644
index 000000000000..84bed1b681c8
--- /dev/null
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/Threads.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.runtimemetrics.java8.internal;
+
+import static java.util.Objects.requireNonNull;
+
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.metrics.Meter;
+import io.opentelemetry.api.metrics.ObservableLongMeasurement;
+import io.opentelemetry.semconv.JvmAttributes;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import javax.annotation.Nullable;
+
+/**
+ * Registers measurements that generate metrics about JVM threads. The metrics generated by this
+ * class follow the
+ * stable JVM metrics semantic conventions.
+ *
+ * This class is internal and is hence not for public use. Its APIs are unstable and can change
+ * at any time.
+ */
+public class Threads {
+
+ // Visible for testing
+ static final Threads INSTANCE = new Threads();
+
+ /** Register observers for java runtime class metrics. */
+ public static List registerObservers(OpenTelemetry openTelemetry) {
+ return INSTANCE.registerObservers(openTelemetry, useThreads());
+ }
+
+ private List registerObservers(OpenTelemetry openTelemetry, boolean useThread) {
+ if (useThread) {
+ return registerObservers(openTelemetry, Threads::getThreads);
+ }
+ return registerObservers(openTelemetry, ManagementFactory.getThreadMXBean());
+ }
+
+ // Visible for testing
+ List registerObservers(OpenTelemetry openTelemetry, ThreadMXBean threadBean) {
+ return registerObservers(
+ openTelemetry,
+ isJava9OrNewer() ? Threads::java9AndNewerCallback : Threads::java8Callback,
+ threadBean);
+ }
+
+ // Visible for testing
+ List registerObservers(
+ OpenTelemetry openTelemetry, Supplier threadSupplier) {
+ return registerObservers(openTelemetry, Threads::java8ThreadCallback, threadSupplier);
+ }
+
+ private static List registerObservers(
+ OpenTelemetry openTelemetry,
+ Function> callbackProvider,
+ T threadInfo) {
+ Meter meter = JmxRuntimeMetricsUtil.getMeter(openTelemetry);
+ List observables = new ArrayList<>();
+
+ observables.add(
+ meter
+ .upDownCounterBuilder("jvm.thread.count")
+ .setDescription("Number of executing platform threads.")
+ .setUnit("{thread}")
+ .buildWithCallback(callbackProvider.apply(threadInfo)));
+
+ return observables;
+ }
+
+ @Nullable private static final MethodHandle THREAD_INFO_IS_DAEMON;
+
+ static {
+ MethodHandle isDaemon;
+ try {
+ isDaemon =
+ MethodHandles.publicLookup()
+ .findVirtual(ThreadInfo.class, "isDaemon", MethodType.methodType(boolean.class));
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ isDaemon = null;
+ }
+ THREAD_INFO_IS_DAEMON = isDaemon;
+ }
+
+ private static boolean isJava9OrNewer() {
+ return THREAD_INFO_IS_DAEMON != null;
+ }
+
+ private static boolean useThreads() {
+ // GraalVM native image does not support ThreadMXBean yet
+ // see https://github.com/oracle/graal/issues/6101
+ boolean isNativeExecution = System.getProperty("org.graalvm.nativeimage.imagecode") != null;
+ return !isJava9OrNewer() || isNativeExecution;
+ }
+
+ private static Consumer java8Callback(ThreadMXBean threadBean) {
+ return measurement -> {
+ int daemonThreadCount = threadBean.getDaemonThreadCount();
+ measurement.record(
+ daemonThreadCount,
+ Attributes.builder().put(JvmAttributes.JVM_THREAD_DAEMON, true).build());
+ measurement.record(
+ threadBean.getThreadCount() - daemonThreadCount,
+ Attributes.builder().put(JvmAttributes.JVM_THREAD_DAEMON, false).build());
+ };
+ }
+
+ private static Consumer java8ThreadCallback(
+ Supplier supplier) {
+ return measurement -> {
+ Map counts = new HashMap<>();
+ for (Thread thread : supplier.get()) {
+ Attributes threadAttributes = threadAttributes(thread);
+ counts.compute(threadAttributes, (k, value) -> value == null ? 1 : value + 1);
+ }
+ counts.forEach((threadAttributes, count) -> measurement.record(count, threadAttributes));
+ };
+ }
+
+ // Visible for testing
+ public static Thread[] getThreads() {
+ ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
+ while (threadGroup.getParent() != null) {
+ threadGroup = threadGroup.getParent();
+ }
+ // use a slightly larger array in case new threads are created
+ int count = threadGroup.activeCount() + 10;
+ Thread[] threads = new Thread[count];
+ int resultSize = threadGroup.enumerate(threads);
+ if (resultSize == threads.length) {
+ return threads;
+ }
+ Thread[] result = new Thread[resultSize];
+ System.arraycopy(threads, 0, result, 0, resultSize);
+ return result;
+ }
+
+ private static Consumer java9AndNewerCallback(
+ ThreadMXBean threadBean) {
+ return measurement -> {
+ Map counts = new HashMap<>();
+ long[] threadIds = threadBean.getAllThreadIds();
+ for (ThreadInfo threadInfo : threadBean.getThreadInfo(threadIds)) {
+ if (threadInfo == null) {
+ continue;
+ }
+ Attributes threadAttributes = threadAttributes(threadInfo);
+ counts.compute(threadAttributes, (k, value) -> value == null ? 1 : value + 1);
+ }
+ counts.forEach((threadAttributes, count) -> measurement.record(count, threadAttributes));
+ };
+ }
+
+ private static Attributes threadAttributes(ThreadInfo threadInfo) {
+ boolean isDaemon;
+ try {
+ isDaemon = (boolean) requireNonNull(THREAD_INFO_IS_DAEMON).invoke(threadInfo);
+ } catch (Throwable e) {
+ throw new IllegalStateException("Unexpected error happened during ThreadInfo#isDaemon()", e);
+ }
+ String threadState = threadInfo.getThreadState().name().toLowerCase(Locale.ROOT);
+ return Attributes.of(
+ JvmAttributes.JVM_THREAD_DAEMON, isDaemon, JvmAttributes.JVM_THREAD_STATE, threadState);
+ }
+
+ private static Attributes threadAttributes(Thread thread) {
+ boolean isDaemon = thread.isDaemon();
+ String threadState = thread.getState().name().toLowerCase(Locale.ROOT);
+ return Attributes.of(
+ JvmAttributes.JVM_THREAD_DAEMON, isDaemon, JvmAttributes.JVM_THREAD_STATE, threadState);
+ }
+
+ protected Threads() {}
+}
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/ClassesStableSemconvTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ClassesTest.java
similarity index 97%
rename from instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/ClassesStableSemconvTest.java
rename to instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ClassesTest.java
index e6fa5c9f1dec..5399e84efa47 100644
--- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/ClassesStableSemconvTest.java
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ClassesTest.java
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-package io.opentelemetry.instrumentation.runtimemetrics.java8;
+package io.opentelemetry.instrumentation.runtimemetrics.java8.internal;
import static io.opentelemetry.instrumentation.runtimemetrics.java8.ScopeUtil.EXPECTED_SCOPE;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
@@ -20,7 +20,7 @@
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
-class ClassesStableSemconvTest {
+class ClassesTest {
@RegisterExtension
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/CpuStableSemconvTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/CpuTest.java
similarity index 97%
rename from instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/CpuStableSemconvTest.java
rename to instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/CpuTest.java
index 507602d62435..c61100718eb3 100644
--- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/CpuStableSemconvTest.java
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/CpuTest.java
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-package io.opentelemetry.instrumentation.runtimemetrics.java8;
+package io.opentelemetry.instrumentation.runtimemetrics.java8.internal;
import static io.opentelemetry.instrumentation.runtimemetrics.java8.ScopeUtil.EXPECTED_SCOPE;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
@@ -16,7 +16,7 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
-class CpuStableSemconvTest {
+class CpuTest {
@RegisterExtension
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalBufferPoolsTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalBufferPoolsTest.java
index 210992d60f4a..371cb6db116b 100644
--- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalBufferPoolsTest.java
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalBufferPoolsTest.java
@@ -30,6 +30,7 @@
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
+@SuppressWarnings("deprecation") // until ExperimentalBufferPools is renamed
@ExtendWith(MockitoExtension.class)
class ExperimentalBufferPoolsTest {
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalCpuTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalCpuTest.java
index be537e020cdb..81ffe161a709 100644
--- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalCpuTest.java
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalCpuTest.java
@@ -19,6 +19,7 @@
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
+@SuppressWarnings("deprecation") // until ExperimentalCpu is renamed
@ExtendWith(MockitoExtension.class)
class ExperimentalCpuTest {
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalFileDescriptorTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalFileDescriptorTest.java
index d54f3ae2b1fb..33cec0bd3194 100644
--- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalFileDescriptorTest.java
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalFileDescriptorTest.java
@@ -22,6 +22,7 @@
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
+@SuppressWarnings("deprecation") // until ExperimentalFileDescriptor is renamed
@ExtendWith(MockitoExtension.class)
class ExperimentalFileDescriptorTest {
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalMemoryPoolsTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalMemoryPoolsTest.java
index 80e3990d8973..2649a01d250f 100644
--- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalMemoryPoolsTest.java
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalMemoryPoolsTest.java
@@ -26,6 +26,7 @@
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
+@SuppressWarnings("deprecation") // until ExperimentalMemoryPools is renamed
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class ExperimentalMemoryPoolsTest {
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/GarbageCollectorTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/GarbageCollectorTest.java
similarity index 98%
rename from instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/GarbageCollectorTest.java
rename to instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/GarbageCollectorTest.java
index bb5f18a3069e..f6fe2a7a115d 100644
--- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/GarbageCollectorTest.java
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/GarbageCollectorTest.java
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-package io.opentelemetry.instrumentation.runtimemetrics.java8;
+package io.opentelemetry.instrumentation.runtimemetrics.java8.internal;
import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static io.opentelemetry.instrumentation.runtimemetrics.java8.ScopeUtil.EXPECTED_SCOPE;
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/MemoryPoolsStableSemconvTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/MemoryPoolsTest.java
similarity index 99%
rename from instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/MemoryPoolsStableSemconvTest.java
rename to instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/MemoryPoolsTest.java
index 391bdb7f10c9..1dc3a438c95d 100644
--- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/MemoryPoolsStableSemconvTest.java
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/MemoryPoolsTest.java
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-package io.opentelemetry.instrumentation.runtimemetrics.java8;
+package io.opentelemetry.instrumentation.runtimemetrics.java8.internal;
import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static io.opentelemetry.instrumentation.runtimemetrics.java8.ScopeUtil.EXPECTED_SCOPE;
@@ -38,7 +38,7 @@
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
-class MemoryPoolsStableSemconvTest {
+class MemoryPoolsTest {
@RegisterExtension
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/ThreadsStableSemconvTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ThreadsTest.java
similarity index 98%
rename from instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/ThreadsStableSemconvTest.java
rename to instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ThreadsTest.java
index 7d9eb8aa994e..09b42fe3d2b8 100644
--- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/ThreadsStableSemconvTest.java
+++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ThreadsTest.java
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-package io.opentelemetry.instrumentation.runtimemetrics.java8;
+package io.opentelemetry.instrumentation.runtimemetrics.java8.internal;
import static io.opentelemetry.instrumentation.runtimemetrics.java8.ScopeUtil.EXPECTED_SCOPE;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
@@ -33,7 +33,7 @@
import org.mockito.stubbing.Answer;
@ExtendWith(MockitoExtension.class)
-class ThreadsStableSemconvTest {
+class ThreadsTest {
@RegisterExtension
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();