From 8e4132fa8dd8cabca07788b9f1e5f07d77fc9ea7 Mon Sep 17 00:00:00 2001 From: jack-berg Date: Thu, 19 May 2022 11:02:38 -0500 Subject: [PATCH 1/2] Add jvm threads instrumentation --- .../RuntimeMetricsInstaller.java | 2 + .../runtimemetrics/RuntimeMetricsTest.groovy | 1 + .../runtimemetrics/Threads.java | 51 +++++++++++++++++++ .../runtimemetrics/ThreadsTest.java | 50 ++++++++++++++++++ 4 files changed, 104 insertions(+) create mode 100644 instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Threads.java create mode 100644 instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ThreadsTest.java diff --git a/instrumentation/runtime-metrics/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/RuntimeMetricsInstaller.java b/instrumentation/runtime-metrics/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/RuntimeMetricsInstaller.java index a55aabe5642e..5d25d63df2c4 100644 --- a/instrumentation/runtime-metrics/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/RuntimeMetricsInstaller.java +++ b/instrumentation/runtime-metrics/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/RuntimeMetricsInstaller.java @@ -10,6 +10,7 @@ import io.opentelemetry.instrumentation.api.config.Config; import io.opentelemetry.instrumentation.runtimemetrics.GarbageCollector; import io.opentelemetry.instrumentation.runtimemetrics.MemoryPools; +import io.opentelemetry.instrumentation.runtimemetrics.Threads; import io.opentelemetry.javaagent.extension.AgentListener; import io.opentelemetry.javaagent.tooling.config.AgentConfig; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; @@ -28,6 +29,7 @@ public void afterAgent(Config config, AutoConfiguredOpenTelemetrySdk unused) { .isInstrumentationEnabled(Collections.singleton("runtime-metrics"), DEFAULT_ENABLED)) { MemoryPools.registerObservers(GlobalOpenTelemetry.get()); + Threads.registerObservers(GlobalOpenTelemetry.get()); if (config.getBoolean( "otel.instrumentation.runtime-metrics.experimental-metrics.enabled", false)) { diff --git a/instrumentation/runtime-metrics/javaagent/src/test/groovy/io/opentelemetry/instrumentation/javaagent/runtimemetrics/RuntimeMetricsTest.groovy b/instrumentation/runtime-metrics/javaagent/src/test/groovy/io/opentelemetry/instrumentation/javaagent/runtimemetrics/RuntimeMetricsTest.groovy index 4e66df5e090a..a471bbde3a5b 100644 --- a/instrumentation/runtime-metrics/javaagent/src/test/groovy/io/opentelemetry/instrumentation/javaagent/runtimemetrics/RuntimeMetricsTest.groovy +++ b/instrumentation/runtime-metrics/javaagent/src/test/groovy/io/opentelemetry/instrumentation/javaagent/runtimemetrics/RuntimeMetricsTest.groovy @@ -22,6 +22,7 @@ class RuntimeMetricsTest extends AgentInstrumentationSpecification { assert getMetrics().any { it.name == "process.runtime.jvm.memory.usage" } assert getMetrics().any { it.name == "process.runtime.jvm.memory.committed" } assert getMetrics().any { it.name == "process.runtime.jvm.memory.max" } + assert getMetrics().any { it.name == "process.runtime.jvm.threads.count" } } } } diff --git a/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Threads.java b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Threads.java new file mode 100644 index 000000000000..95805bb63c3b --- /dev/null +++ b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Threads.java @@ -0,0 +1,51 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.runtimemetrics; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.metrics.Meter; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; + +/** + * Registers measurements that generate metrics about JVM threads. + * + *

Example usage: + * + *

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

Example metrics being exported: + * + *

+ *   process.runtime.jvm.threads.count 4
+ * 
+ */ +public final class Threads { + + // Visible for testing + static final Threads INSTANCE = new Threads(); + + /** Register observers for java runtime class metrics. */ + public static void registerObservers(OpenTelemetry openTelemetry) { + INSTANCE.registerObservers(openTelemetry, ManagementFactory.getThreadMXBean()); + } + + // Visible for testing + void registerObservers(OpenTelemetry openTelemetry, ThreadMXBean threadBean) { + Meter meter = openTelemetry.getMeter("io.opentelemetry.runtime-metrics"); + + meter + .counterBuilder("process.runtime.jvm.threads.count") + .setDescription("Number of executing threads") + .setUnit("1") + .buildWithCallback( + observableMeasurement -> observableMeasurement.record(threadBean.getThreadCount())); + } + + private Threads() {} +} diff --git a/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ThreadsTest.java b/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ThreadsTest.java new file mode 100644 index 000000000000..584e38eb2bf1 --- /dev/null +++ b/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ThreadsTest.java @@ -0,0 +1,50 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.runtimemetrics; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static org.mockito.Mockito.when; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import java.lang.management.ThreadMXBean; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class ThreadsTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Mock private ThreadMXBean threadBean; + + @Test + void registerObservers() { + when(threadBean.getThreadCount()).thenReturn(3); + + Threads.INSTANCE.registerObservers(testing.getOpenTelemetry(), threadBean); + + testing.waitAndAssertMetrics( + "io.opentelemetry.runtime-metrics", + "process.runtime.jvm.threads.count", + metrics -> + metrics.anySatisfy( + metricData -> + assertThat(metricData) + .hasDescription("Number of executing threads") + .hasUnit("1") + .hasLongSumSatisfying( + sum -> + sum.hasPointsSatisfying( + point -> + point.hasValue(3).hasAttributes(Attributes.empty()))))); + } +} From 77cd04b61214b38b36add82aa4610f91615f4b06 Mon Sep 17 00:00:00 2001 From: jack-berg Date: Thu, 19 May 2022 14:46:22 -0500 Subject: [PATCH 2/2] Use up down counter --- .../instrumentation/runtimemetrics/Threads.java | 2 +- .../instrumentation/runtimemetrics/ThreadsTest.java | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Threads.java b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Threads.java index 95805bb63c3b..dc868ad89147 100644 --- a/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Threads.java +++ b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Threads.java @@ -40,7 +40,7 @@ void registerObservers(OpenTelemetry openTelemetry, ThreadMXBean threadBean) { Meter meter = openTelemetry.getMeter("io.opentelemetry.runtime-metrics"); meter - .counterBuilder("process.runtime.jvm.threads.count") + .upDownCounterBuilder("process.runtime.jvm.threads.count") .setDescription("Number of executing threads") .setUnit("1") .buildWithCallback( diff --git a/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ThreadsTest.java b/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ThreadsTest.java index 584e38eb2bf1..b1128ce41faa 100644 --- a/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ThreadsTest.java +++ b/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ThreadsTest.java @@ -43,8 +43,9 @@ void registerObservers() { .hasUnit("1") .hasLongSumSatisfying( sum -> - sum.hasPointsSatisfying( - point -> - point.hasValue(3).hasAttributes(Attributes.empty()))))); + sum.isNotMonotonic() + .hasPointsSatisfying( + point -> + point.hasValue(3).hasAttributes(Attributes.empty()))))); } }