diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/RuntimeMetricsBuilder.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/RuntimeMetricsBuilder.java index e73c535e5803..63a2b4b8fe72 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/RuntimeMetricsBuilder.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/RuntimeMetricsBuilder.java @@ -14,6 +14,7 @@ import io.opentelemetry.instrumentation.runtimemetrics.java8.Threads; import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalBufferPools; import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalCpu; +import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalFileDescriptor; import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalMemoryPools; import java.util.ArrayList; import java.util.Arrays; @@ -114,6 +115,7 @@ private List buildObservables() { observables.addAll(ExperimentalBufferPools.registerObservers(openTelemetry)); observables.addAll(ExperimentalCpu.registerObservers(openTelemetry)); observables.addAll(ExperimentalMemoryPools.registerObservers(openTelemetry)); + observables.addAll(ExperimentalFileDescriptor.registerObservers(openTelemetry)); } return observables; } catch (Exception e) { diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java8/Java8RuntimeMetricsInstaller.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java8/Java8RuntimeMetricsInstaller.java index 57ca0396ee99..799a6ac71778 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java8/Java8RuntimeMetricsInstaller.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java8/Java8RuntimeMetricsInstaller.java @@ -15,6 +15,7 @@ import io.opentelemetry.instrumentation.runtimemetrics.java8.Threads; import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalBufferPools; import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalCpu; +import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalFileDescriptor; import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalMemoryPools; import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsUtil; import io.opentelemetry.javaagent.extension.AgentListener; @@ -51,6 +52,7 @@ public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) { observables.addAll(ExperimentalBufferPools.registerObservers(openTelemetry)); observables.addAll(ExperimentalCpu.registerObservers(openTelemetry)); observables.addAll(ExperimentalMemoryPools.registerObservers(openTelemetry)); + observables.addAll(ExperimentalFileDescriptor.registerObservers(openTelemetry)); } Thread cleanupTelemetry = new Thread(() -> JmxRuntimeMetricsUtil.closeObservers(observables)); 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..ad844668441b 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 @@ -7,8 +7,8 @@ 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 io.opentelemetry.instrumentation.runtimemetrics.java8.internal.OperatingSystemMethods; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; @@ -48,8 +48,8 @@ public static List registerObservers(OpenTelemetry openTelemetry) return INSTANCE.registerObservers( openTelemetry, Runtime.getRuntime()::availableProcessors, - CpuMethods.processCpuTime(), - CpuMethods.processCpuUtilization()); + OperatingSystemMethods.processCpuTime(), + OperatingSystemMethods.processCpuUtilization()); } // Visible for testing 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..a297599f902d 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 @@ -27,7 +27,7 @@ public static List registerObservers(OpenTelemetry openTelemetry) return registerObservers( openTelemetry, ManagementFactory.getOperatingSystemMXBean(), - CpuMethods.systemCpuUtilization()); + OperatingSystemMethods.systemCpuUtilization()); } // Visible for testing 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 new file mode 100644 index 000000000000..eabe2fe8663a --- /dev/null +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalFileDescriptor.java @@ -0,0 +1,71 @@ +/* + * 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.function.Supplier; + +/** + * Registers measurements that generate experimental metrics about file descriptor. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public class ExperimentalFileDescriptor { + /** Register observers for java runtime experimental file descriptor metrics. */ + public static List registerObservers(OpenTelemetry openTelemetry) { + return registerObservers( + openTelemetry, + OperatingSystemMethods.openFileDescriptorCount(), + OperatingSystemMethods.maxFileDescriptorCount()); + } + + // Visible for testing + static List registerObservers( + OpenTelemetry openTelemetry, + Supplier openFileDescriptorCount, + Supplier maxFileDescriptorCount) { + Meter meter = JmxRuntimeMetricsUtil.getMeter(openTelemetry); + List observables = new ArrayList<>(); + + if (openFileDescriptorCount != null) { + observables.add( + meter + .upDownCounterBuilder("process.open_file_descriptor.count") + .setDescription("Number of file descriptors in use by the process.") + .setUnit("{count}") + .buildWithCallback( + observableMeasurement -> { + Long openCount = openFileDescriptorCount.get(); + if (openCount != null && openCount >= 0) { + observableMeasurement.record(openCount); + } + })); + } + + if (maxFileDescriptorCount != null) { + observables.add( + meter + .upDownCounterBuilder("process.open_file_descriptor.limit") + .setDescription("Measure of max file descriptors.") + .setUnit("{count}") + .buildWithCallback( + observableMeasurement -> { + Long maxCount = maxFileDescriptorCount.get(); + if (maxCount != null && maxCount >= 0) { + observableMeasurement.record(maxCount); + } + })); + } + + return observables; + } + + private ExperimentalFileDescriptor() {} +} diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/CpuMethods.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/OperatingSystemMethods.java similarity index 57% rename from instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/CpuMethods.java rename to instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/OperatingSystemMethods.java index 479170865901..9ab782d560c7 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/CpuMethods.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/OperatingSystemMethods.java @@ -16,38 +16,33 @@ * This class is internal and is hence not for public use. Its APIs are unstable and can change at * any time. */ -public final class CpuMethods { +public final class OperatingSystemMethods { private static final String OS_BEAN_J9 = "com.ibm.lang.management.OperatingSystemMXBean"; private static final String OS_BEAN_HOTSPOT = "com.sun.management.OperatingSystemMXBean"; + private static final String UNIX_OS_BEAN_J9 = "com.ibm.lang.management.UnixOperatingSystemMXBean"; + private static final String UNIX_OS_BEAN_HOTSPOT = "com.sun.management.UnixOperatingSystemMXBean"; private static final String METHOD_PROCESS_CPU_TIME = "getProcessCpuTime"; private static final String METHOD_PROCESS_CPU_LOAD = "getProcessCpuLoad"; private static final String METHOD_CPU_LOAD = "getCpuLoad"; private static final String METHOD_SYSTEM_CPU_LOAD = "getSystemCpuLoad"; + private static final String METHOD_OPEN_FILE_DESCRIPTOR_COUNT = "getOpenFileDescriptorCount"; + private static final String METHOD_MAX_FILE_DESCRIPTOR_COUNT = "getMaxFileDescriptorCount"; @Nullable private static final Supplier processCpuTime; @Nullable private static final Supplier processCpuUtilization; @Nullable private static final Supplier systemCpuUtilization; + @Nullable private static final Supplier openFileDescriptorCount; + @Nullable private static final Supplier maxFileDescriptorCount; static { OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean(); - Supplier processCpuTimeSupplier = - methodInvoker(osBean, OS_BEAN_HOTSPOT, METHOD_PROCESS_CPU_TIME, Long.class); - if (processCpuTimeSupplier == null) { - // More users will be on hotspot than j9, so check for j9 second - processCpuTimeSupplier = - methodInvoker(osBean, OS_BEAN_J9, METHOD_PROCESS_CPU_TIME, Long.class); - } - processCpuTime = processCpuTimeSupplier; + processCpuTime = + findMethod(osBean, OS_BEAN_HOTSPOT, OS_BEAN_J9, METHOD_PROCESS_CPU_TIME, Long.class); - Supplier processCpuSupplier = - methodInvoker(osBean, OS_BEAN_HOTSPOT, METHOD_PROCESS_CPU_LOAD, Double.class); - if (processCpuSupplier == null) { - // More users will be on hotspot than j9, so check for j9 second - processCpuSupplier = methodInvoker(osBean, OS_BEAN_J9, METHOD_PROCESS_CPU_LOAD, Double.class); - } - processCpuUtilization = processCpuSupplier; + processCpuUtilization = + findMethod(osBean, OS_BEAN_HOTSPOT, OS_BEAN_J9, METHOD_PROCESS_CPU_LOAD, Double.class); // As of java 14, com.sun.management.OperatingSystemMXBean#getCpuLoad() is preferred and // #getSystemCpuLoad() is deprecated @@ -55,13 +50,25 @@ public final class CpuMethods { methodInvoker(osBean, OS_BEAN_HOTSPOT, METHOD_CPU_LOAD, Double.class); if (systemCpuSupplier == null) { systemCpuSupplier = - methodInvoker(osBean, OS_BEAN_HOTSPOT, METHOD_SYSTEM_CPU_LOAD, Double.class); - } - if (systemCpuSupplier == null) { - // More users will be on hotspot than j9, so check for j9 second - systemCpuSupplier = methodInvoker(osBean, OS_BEAN_J9, METHOD_SYSTEM_CPU_LOAD, Double.class); + findMethod(osBean, OS_BEAN_HOTSPOT, OS_BEAN_J9, METHOD_SYSTEM_CPU_LOAD, Double.class); } systemCpuUtilization = systemCpuSupplier; + + openFileDescriptorCount = + findMethod( + osBean, + UNIX_OS_BEAN_HOTSPOT, + UNIX_OS_BEAN_J9, + METHOD_OPEN_FILE_DESCRIPTOR_COUNT, + Long.class); + + maxFileDescriptorCount = + findMethod( + osBean, + UNIX_OS_BEAN_HOTSPOT, + UNIX_OS_BEAN_J9, + METHOD_MAX_FILE_DESCRIPTOR_COUNT, + Long.class); } @Nullable @@ -87,6 +94,22 @@ private static Supplier methodInvoker( } } + // judge whether use hotspots or openj9 + private static Supplier findMethod( + OperatingSystemMXBean osBean, + String osBeanClassName, + String osBeanJ9ClassName, + String methodName, + Class returnType) { + Supplier processSupplier = methodInvoker(osBean, osBeanClassName, methodName, returnType); + if (processSupplier == null) { + // More users will be on hotspot than j9, so check for j9 second + processSupplier = methodInvoker(osBean, osBeanJ9ClassName, methodName, returnType); + } + + return processSupplier; + } + public static Supplier processCpuTime() { return processCpuTime; } @@ -99,5 +122,13 @@ public static Supplier systemCpuUtilization() { return systemCpuUtilization; } - private CpuMethods() {} + public static Supplier openFileDescriptorCount() { + return openFileDescriptorCount; + } + + public static Supplier maxFileDescriptorCount() { + return maxFileDescriptorCount; + } + + private OperatingSystemMethods() {} } 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 new file mode 100644 index 000000000000..dc35e667409e --- /dev/null +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/java8/internal/ExperimentalFileDescriptorTest.java @@ -0,0 +1,54 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +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; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import java.util.function.Supplier; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class ExperimentalFileDescriptorTest { + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Test + void registerObservers() { + Supplier openFileDescriptor = () -> 10L; + Supplier maxFileDescriptor = () -> 10000L; + + ExperimentalFileDescriptor.registerObservers( + testing.getOpenTelemetry(), openFileDescriptor, maxFileDescriptor); + + testing.waitAndAssertMetrics( + "io.opentelemetry.runtime-telemetry-java8", + "process.open_file_descriptor.count", + metrics -> + metrics.anySatisfy( + metricData -> + assertThat(metricData) + .hasInstrumentationScope(EXPECTED_SCOPE) + .hasDescription("Number of file descriptors in use by the process.") + .hasUnit("{count}") + .hasLongSumSatisfying( + sum -> sum.hasPointsSatisfying(point -> point.hasValue(10L))))); + testing.waitAndAssertMetrics( + "io.opentelemetry.runtime-telemetry-java8", + "process.open_file_descriptor.limit", + metrics -> + metrics.anySatisfy( + metricData -> + assertThat(metricData) + .hasInstrumentationScope(EXPECTED_SCOPE) + .hasDescription("Measure of max file descriptors.") + .hasUnit("{count}") + .hasLongSumSatisfying( + sum -> sum.hasPointsSatisfying(point -> point.hasValue(10000L))))); + } +}