diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/README.md b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/README.md index 3d68c3679aad..7ddde09e1fa6 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/README.md +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/README.md @@ -29,16 +29,56 @@ runtimeOnly("io.opentelemetry.instrumentation:opentelemetry-runtime-telemetry-ja ### Usage -Register observers for the desired runtime metrics: +Register JVM runtime metrics: ```java OpenTelemetry openTelemetry = // OpenTelemetry instance configured elsewhere -Classes.registerObservers(openTelemetry); -Cpu.registerObservers(openTelemetry); -MemoryPools.registerObservers(openTelemetry); -Threads.registerObservers(openTelemetry); -GarbageCollector.registerObservers(openTelemetry); +RuntimeMetrics runtimeMetrics = RuntimeMetrics.create(openTelemetry); + +// When done, close to stop metric collection +runtimeMetrics.close(); +``` + +To select specific metrics, configure [metric views](https://opentelemetry.io/docs/languages/java/sdk/#views) +on the SDK to filter or customize which metrics are exported. + +For example, using [declarative configuration](https://github.com/open-telemetry/opentelemetry-java-examples/tree/main/declarative-configuration): + +```yaml +meter_provider: + views: + # Drop jvm.memory.committed metric + - selector: + instrument_name: jvm.memory.committed + stream: + aggregation: + drop: + # Only retain jvm.memory.type attribute on jvm.memory.used + - selector: + instrument_name: jvm.memory.used + stream: + attribute_keys: + included: + - jvm.memory.type +``` + +To retain only `jvm.memory.used` and drop all other JVM runtime metrics: + +```yaml +meter_provider: + views: + # Drop all metrics from this instrumentation scope + - selector: + meter_name: io.opentelemetry.runtime-telemetry-java8 + stream: + aggregation: + drop: + # Keep jvm.memory.used (views are additive, this creates a second stream) + - selector: + meter_name: io.opentelemetry.runtime-telemetry-java8 + instrument_name: jvm.memory.used + stream: {} ``` ## Garbage Collector Dependent Metrics diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/Classes.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/Classes.java index 0632ad5a3e4d..51dc8c6ad32a 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/Classes.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/Classes.java @@ -6,74 +6,21 @@ package io.opentelemetry.instrumentation.runtimemetrics.java8; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.metrics.Meter; -import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsUtil; -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. + * Registers measurements that generate metrics about JVM classes. * - *

Example usage: - * - *

{@code
- * Classes.registerObservers(GlobalOpenTelemetry.get());
- * }
- * - *

Example metrics being exported: - * - *

- *   jvm.class.loaded 100
- *   jvm.class.unloaded 2
- *   jvm.class.count 98
- * 
+ * @deprecated Use {@link RuntimeMetrics} instead, and configure metric views to select specific + * metrics. */ +@Deprecated public final 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; + return io.opentelemetry.instrumentation.runtimemetrics.java8.internal.Classes.registerObservers( + openTelemetry); } private Classes() {} diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/Cpu.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/Cpu.java index 1263c1b46560..747bba1b0d32 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/Cpu.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/Cpu.java @@ -6,100 +6,21 @@ package io.opentelemetry.instrumentation.runtimemetrics.java8; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.metrics.Meter; -import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.CpuMethods; -import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsUtil; -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. + * Registers measurements that generate metrics about CPU. * - *

Example usage: - * - *

{@code
- * Cpu.registerObservers(GlobalOpenTelemetry.get());
- * }
- * - *

Example metrics being exported: - * - *

- *   jvm.cpu.time 20.42
- *   jvm.cpu.count 8
- *   jvm.cpu.recent_utilization 0.1
- * 
+ * @deprecated Use {@link RuntimeMetrics} instead, and configure metric views to select specific + * metrics. */ +@Deprecated public final 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; + return io.opentelemetry.instrumentation.runtimemetrics.java8.internal.Cpu.registerObservers( + openTelemetry); } private Cpu() {} diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/GarbageCollector.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/GarbageCollector.java index b5de4e922ed1..bad64715e2dc 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/GarbageCollector.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/GarbageCollector.java @@ -5,170 +5,23 @@ package io.opentelemetry.instrumentation.runtimemetrics.java8; -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.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsUtil; -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. - * - *

Example usage: - * - *

{@code
- * GarbageCollector.registerObservers(GlobalOpenTelemetry.get());
- * }
+ * Registers instruments that generate metrics about JVM garbage collection. * - *

Example metrics being exported: - * - *

- *   jvm.gc.duration{jvm.gc.name="G1 Young Generation",jvm.gc.action="end of minor GC"} 0.022
- * 
+ * @deprecated Use {@link RuntimeMetrics} instead, and configure metric views to select specific + * metrics. */ +@Deprecated public final class GarbageCollector { - private static final Logger logger = Logger.getLogger(GarbageCollector.class.getName()); - - private static final double MILLIS_PER_S = TimeUnit.SECONDS.toMillis(1); - - 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 - 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; - } + return io.opentelemetry.instrumentation.runtimemetrics.java8.internal.GarbageCollector + .registerObservers(openTelemetry, captureGcCause); } private GarbageCollector() {} diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/MemoryPools.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/MemoryPools.java index 0a7228a2894c..dddc6295cff4 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/MemoryPools.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/MemoryPools.java @@ -6,151 +6,21 @@ package io.opentelemetry.instrumentation.runtimemetrics.java8; 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.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsUtil; -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. + * Registers measurements that generate metrics about JVM memory pools. * - *

Example usage: - * - *

{@code
- * MemoryPools.registerObservers(GlobalOpenTelemetry.get());
- * }
- * - *

Example metrics being exported: - * - *

- *   jvm.memory.used{type="heap",pool="G1 Eden Space"} 2500000
- *   jvm.memory.committed{type="heap",pool="G1 Eden Space"} 3000000
- *   jvm.memory.limit{type="heap",pool="G1 Eden Space"} 4000000
- *   jvm.memory.used_after_last_gc{type="heap",pool="G1 Eden Space"} 1500000
- *   jvm.memory.used{type="non_heap",pool="Metaspace"} 400
- *   jvm.memory.committed{type="non_heap",pool="Metaspace"} 500
- * 
+ * @deprecated Use {@link RuntimeMetrics} instead, and configure metric views to select specific + * metrics. */ +@Deprecated public final class MemoryPools { /** Register observers for java runtime memory metrics. */ public static List registerObservers(OpenTelemetry openTelemetry) { - return registerObservers(openTelemetry, ManagementFactory.getMemoryPoolMXBeans()); - } - - // Visible for testing - 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 - 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"; + return io.opentelemetry.instrumentation.runtimemetrics.java8.internal.MemoryPools + .registerObservers(openTelemetry); } private MemoryPools() {} diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/Threads.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/Threads.java index 204eff083329..a2c43555434b 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/Threads.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/Threads.java @@ -5,199 +5,22 @@ package io.opentelemetry.instrumentation.runtimemetrics.java8; -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.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsUtil; -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. - * - *

Example usage: - * - *

{@code
- * Threads.registerObservers(GlobalOpenTelemetry.get());
- * }
+ * Registers measurements that generate metrics about JVM threads. * - *

Example metrics being exported: - * - *

- *   jvm.thread.count{jvm.thread.daemon=true,jvm.thread.state="waiting"} 1
- *   jvm.thread.count{jvm.thread.daemon=true,jvm.thread.state="runnable"} 2
- *   jvm.thread.count{jvm.thread.daemon=false,jvm.thread.state="waiting"} 2
- *   jvm.thread.count{jvm.thread.daemon=false,jvm.thread.state="runnable"} 3
- * 
+ * @deprecated Use {@link RuntimeMetrics} instead, and configure metric views to select specific + * metrics. */ +@Deprecated public final 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 - 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); + 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();