Add meter version to runtime metrics
Runtime metrics doesn't include the meter version. This adds it from the
utility method in the instrumentation-api
`EmbeddedInstrumentationProperties.findVersion`. I know I can read the
properties file for this module, but its repetitive to implement that in
many places.
MODIFY
instrumentation/runtime-metrics/library/build.gradle.kts
instrumentation/runtime-metrics/library/build.gradle.kts
@@ -3,6 +3,8 @@ plugins {
}
dependencies {
+ implementation(project(":instrumentation-api"))
+
testImplementation("io.opentelemetry:opentelemetry-sdk-metrics")
testImplementation(project(":testing-common"))
}

MODIFY
instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/BufferPools.java
instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/BufferPools.java
@@ -39,10 +39,14 @@ public final class BufferPools {
/** Register observers for java runtime buffer pool metrics. */
public static void registerObservers(OpenTelemetry openTelemetry) {
-
List<BufferPoolMXBean> bufferBeans =
ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class);
- Meter meter = openTelemetry.getMeter("io.opentelemetry.runtime-metrics");
+ registerObservers(openTelemetry, bufferBeans);
+ }
+
+ // Visible for testing
+ static void registerObservers(OpenTelemetry openTelemetry, List<BufferPoolMXBean> bufferBeans) {
+ Meter meter = RuntimeMetricsUtil.getMeter(openTelemetry);
meter
.upDownCounterBuilder("process.runtime.jvm.buffer.usage")
@@ -63,6 +67,7 @@ public final class BufferPools {
.buildWithCallback(callback(bufferBeans, BufferPoolMXBean::getCount));
}
+ // Visible for testing
static Consumer<ObservableLongMeasurement> callback(
List<BufferPoolMXBean> bufferPools, Function<BufferPoolMXBean, Long> extractor) {
List<Attributes> attributeSets = new ArrayList<>(bufferPools.size());

MODIFY
instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Classes.java
instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Classes.java
@@ -39,7 +39,7 @@ public final class Classes {
// Visible for testing
void registerObservers(OpenTelemetry openTelemetry, ClassLoadingMXBean classBean) {
- Meter meter = openTelemetry.getMeter("io.opentelemetry.runtime-metrics");
+ Meter meter = RuntimeMetricsUtil.getMeter(openTelemetry);
meter
.counterBuilder("process.runtime.jvm.classes.loaded")

MODIFY
instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Cpu.java
instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Cpu.java
@@ -80,7 +80,7 @@ public final class Cpu {
OperatingSystemMXBean osBean,
@Nullable Supplier<Double> systemCpuUsage,
@Nullable Supplier<Double> processCpuUsage) {
- Meter meter = openTelemetry.getMeter("io.opentelemetry.runtime-metrics");
+ Meter meter = RuntimeMetricsUtil.getMeter(openTelemetry);
meter
.gaugeBuilder("process.runtime.jvm.system.cpu.load_1m")

MODIFY
instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/GarbageCollector.java
instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/GarbageCollector.java
@@ -36,7 +36,7 @@ public final class GarbageCollector {
/** Register observers for java runtime garbage collector metrics. */
public static void registerObservers(OpenTelemetry openTelemetry) {
List<GarbageCollectorMXBean> garbageCollectors = ManagementFactory.getGarbageCollectorMXBeans();
- Meter meter = openTelemetry.getMeterProvider().get(GarbageCollector.class.getName());
+ Meter meter = RuntimeMetricsUtil.getMeter(openTelemetry);
List<Attributes> labelSets = new ArrayList<>(garbageCollectors.size());
for (GarbageCollectorMXBean gc : garbageCollectors) {
labelSets.add(Attributes.of(GC_KEY, gc.getName()));

MODIFY
instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPools.java
instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPools.java
@@ -50,8 +50,12 @@ public final class MemoryPools {
/** Register observers for java runtime memory metrics. */
public static void registerObservers(OpenTelemetry openTelemetry) {
- List<MemoryPoolMXBean> poolBeans = ManagementFactory.getMemoryPoolMXBeans();
- Meter meter = openTelemetry.getMeter("io.opentelemetry.runtime-metrics");
+ registerObservers(openTelemetry, ManagementFactory.getMemoryPoolMXBeans());
+ }
+
+ // Visible for testing
+ static void registerObservers(OpenTelemetry openTelemetry, List<MemoryPoolMXBean> poolBeans) {
+ Meter meter = RuntimeMetricsUtil.getMeter(openTelemetry);
meter
.upDownCounterBuilder("process.runtime.jvm.memory.usage")

ADD
instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/RuntimeMetricsUtil.java
+/*
+ * 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 io.opentelemetry.api.metrics.MeterBuilder;
+import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties;
+import javax.annotation.Nullable;
+
+class RuntimeMetricsUtil {
+
+ private static final String INSTRUMENTATION_NAME = "io.opentelemetry.runtime-metrics";
+
+ @Nullable
+ private static final String INSTRUMENTATION_VERSION =
+ EmbeddedInstrumentationProperties.findVersion(INSTRUMENTATION_NAME);
+
+ static Meter getMeter(OpenTelemetry openTelemetry) {
+ MeterBuilder meterBuilder = openTelemetry.meterBuilder(INSTRUMENTATION_NAME);
+ if (INSTRUMENTATION_VERSION != null) {
+ meterBuilder.setInstrumentationVersion(INSTRUMENTATION_VERSION);
+ }
+ return meterBuilder.build();
+ }
+
+ private RuntimeMetricsUtil() {}
+}

MODIFY
instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Threads.java
instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/Threads.java
@@ -42,7 +42,7 @@ public final class Threads {
// Visible for testing
void registerObservers(OpenTelemetry openTelemetry, ThreadMXBean threadBean) {
- Meter meter = openTelemetry.getMeter("io.opentelemetry.runtime-metrics");
+ Meter meter = RuntimeMetricsUtil.getMeter(openTelemetry);
meter
.upDownCounterBuilder("process.runtime.jvm.threads.count")

MODIFY
instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/BufferPoolsTest.java
instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/BufferPoolsTest.java
package io.opentelemetry.instrumentation.runtimemetrics;
+import static io.opentelemetry.instrumentation.runtimemetrics.ScopeUtil.EXPECTED_SCOPE;
+import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.ObservableLongMeasurement;
+import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
+import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
import java.lang.management.BufferPoolMXBean;
import java.util.Arrays;
import java.util.List;
@@ -20,12 +25,17 @@ import java.util.function.Consumer;
import org.junit.jupiter.api.BeforeEach;
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.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class BufferPoolsTest {
+
+ @RegisterExtension
+ static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
+
@Spy private ObservableLongMeasurement measurement;
@Mock private BufferPoolMXBean bufferPoolBean;
private List<BufferPoolMXBean> beans;
@@ -36,6 +46,74 @@ public class BufferPoolsTest {
beans = Arrays.asList(bufferPoolBean);
}
+ @Test
+ void registerObservers() {
+ when(bufferPoolBean.getMemoryUsed()).thenReturn(10L);
+ when(bufferPoolBean.getTotalCapacity()).thenReturn(11L);
+ when(bufferPoolBean.getCount()).thenReturn(12L);
+
+ BufferPools.registerObservers(testing.getOpenTelemetry(), beans);
+
+ testing.waitAndAssertMetrics(
+ "io.opentelemetry.runtime-metrics",
+ "process.runtime.jvm.buffer.usage",
+ metrics ->
+ metrics.anySatisfy(
+ metricData ->
+ assertThat(metricData)
+ .hasInstrumentationScope(EXPECTED_SCOPE)
+ .hasDescription(
+ "Memory that the Java virtual machine is using for this buffer pool")
+ .hasUnit("By")
+ .hasLongSumSatisfying(
+ sum ->
+ sum.hasPointsSatisfying(
+ point ->
+ point
+ .hasValue(10)
+ .hasAttribute(
+ AttributeKey.stringKey("pool"),
+ "buffer_pool_1")))));
+ testing.waitAndAssertMetrics(
+ "io.opentelemetry.runtime-metrics",
+ "process.runtime.jvm.buffer.limit",
+ metrics ->
+ metrics.anySatisfy(
+ metricData ->
+ assertThat(metricData)
+ .hasInstrumentationScope(EXPECTED_SCOPE)
+ .hasDescription("Total capacity of the buffers in this pool")
+ .hasUnit("By")
+ .hasLongSumSatisfying(
+ sum ->
+ sum.hasPointsSatisfying(
+ point ->
+ point
+ .hasValue(11)
+ .hasAttribute(
+ AttributeKey.stringKey("pool"),
+ "buffer_pool_1")))));
+ testing.waitAndAssertMetrics(
+ "io.opentelemetry.runtime-metrics",
+ "process.runtime.jvm.buffer.count",
+ metrics ->
+ metrics.anySatisfy(
+ metricData ->
+ assertThat(metricData)
+ .hasInstrumentationScope(EXPECTED_SCOPE)
+ .hasDescription("The number of buffers in the pool")
+ .hasUnit("buffers")
+ .hasLongSumSatisfying(
+ sum ->
+ sum.hasPointsSatisfying(
+ point ->
+ point
+ .hasValue(12)
+ .hasAttribute(
+ AttributeKey.stringKey("pool"),
+ "buffer_pool_1")))));
+ }
+
@Test
void callback_Records() {
when(bufferPoolBean.getMemoryUsed()).thenReturn(1L);

MODIFY
instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ClassesTest.java
instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ClassesTest.java
package io.opentelemetry.instrumentation.runtimemetrics;
+import static io.opentelemetry.instrumentation.runtimemetrics.ScopeUtil.EXPECTED_SCOPE;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.mockito.Mockito.when;
@@ -41,6 +42,7 @@ class ClassesTest {
metrics.anySatisfy(
metricData ->
assertThat(metricData)
+ .hasInstrumentationScope(EXPECTED_SCOPE)
.hasDescription("Number of classes loaded since JVM start")
.hasUnit("1")
.hasLongSumSatisfying(
@@ -56,6 +58,7 @@ class ClassesTest {
metrics.anySatisfy(
metricData ->
assertThat(metricData)
+ .hasInstrumentationScope(EXPECTED_SCOPE)
.hasDescription("Number of classes unloaded since JVM start")
.hasUnit("1")
.hasLongSumSatisfying(
@@ -71,6 +74,7 @@ class ClassesTest {
metrics.anySatisfy(
metricData ->
assertThat(metricData)
+ .hasInstrumentationScope(EXPECTED_SCOPE)
.hasDescription("Number of classes currently loaded")
.hasUnit("1")
.hasLongSumSatisfying(

MODIFY
instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/CpuTest.java
instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/CpuTest.java
package io.opentelemetry.instrumentation.runtimemetrics;
+import static io.opentelemetry.instrumentation.runtimemetrics.ScopeUtil.EXPECTED_SCOPE;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.mockito.Mockito.when;
@@ -42,6 +43,7 @@ class CpuTest {
metrics.anySatisfy(
metricData ->
assertThat(metricData)
+ .hasInstrumentationScope(EXPECTED_SCOPE)
.hasDescription("Average CPU load of the whole system for the last minute")
.hasUnit("1")
.hasDoubleGaugeSatisfying(
@@ -53,6 +55,7 @@ class CpuTest {
metrics.anySatisfy(
metricData ->
assertThat(metricData)
+ .hasInstrumentationScope(EXPECTED_SCOPE)
.hasDescription("Recent cpu utilization for the whole system")
.hasUnit("1")
.hasDoubleGaugeSatisfying(
@@ -64,6 +67,7 @@ class CpuTest {
metrics.anySatisfy(
metricData ->
assertThat(metricData)
+ .hasInstrumentationScope(EXPECTED_SCOPE)
.hasDescription("Recent cpu utilization for the process")
.hasUnit("1")
.hasDoubleGaugeSatisfying(

MODIFY
instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPoolsTest.java
instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPoolsTest.java
package io.opentelemetry.instrumentation.runtimemetrics;
+import static io.opentelemetry.instrumentation.runtimemetrics.ScopeUtil.EXPECTED_SCOPE;
+import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.ObservableLongMeasurement;
+import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
+import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryType;
import java.lang.management.MemoryUsage;
@@ -22,6 +27,7 @@ import java.util.function.Consumer;
import org.junit.jupiter.api.BeforeEach;
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.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
@@ -29,6 +35,9 @@ import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class MemoryPoolsTest {
+ @RegisterExtension
+ static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
+
@Spy private ObservableLongMeasurement measurement;
@Mock private MemoryPoolMXBean heapPoolBean;
@@ -50,6 +59,125 @@ class MemoryPoolsTest {
beans = Arrays.asList(heapPoolBean, nonHeapPoolBean);
}
+ @Test
+ void registerObservers() {
+ when(heapPoolUsage.getInit()).thenReturn(10L);
+ when(heapPoolUsage.getUsed()).thenReturn(11L);
+ when(heapPoolUsage.getCommitted()).thenReturn(12L);
+ when(heapPoolUsage.getMax()).thenReturn(13L);
+ when(nonHeapUsage.getInit()).thenReturn(14L);
+ when(nonHeapUsage.getUsed()).thenReturn(15L);
+ when(nonHeapUsage.getCommitted()).thenReturn(16L);
+ when(nonHeapUsage.getMax()).thenReturn(17L);
+
+ MemoryPools.registerObservers(testing.getOpenTelemetry(), beans);
+
+ testing.waitAndAssertMetrics(
+ "io.opentelemetry.runtime-metrics",
+ "process.runtime.jvm.memory.init",
+ metrics ->
+ metrics.anySatisfy(
+ metricData ->
+ assertThat(metricData)
+ .hasInstrumentationScope(EXPECTED_SCOPE)
+ .hasDescription("Measure of initial memory requested")
+ .hasUnit("By")
+ .hasLongSumSatisfying(
+ sum ->
+ sum.hasPointsSatisfying(
+ point ->
+ point
+ .hasValue(10)
+ .hasAttribute(
+ AttributeKey.stringKey("pool"), "heap_pool")
+ .hasAttribute(AttributeKey.stringKey("type"), "heap"),
+ point ->
+ point
+ .hasValue(14)
+ .hasAttribute(
+ AttributeKey.stringKey("pool"), "non_heap_pool")
+ .hasAttribute(
+ AttributeKey.stringKey("type"), "non_heap")))));
+ testing.waitAndAssertMetrics(
+ "io.opentelemetry.runtime-metrics",
+ "process.runtime.jvm.memory.usage",
+ metrics ->
+ metrics.anySatisfy(
+ metricData ->
+ assertThat(metricData)
+ .hasInstrumentationScope(EXPECTED_SCOPE)
+ .hasDescription("Measure of memory used")
+ .hasUnit("By")
+ .hasLongSumSatisfying(
+ sum ->
+ sum.hasPointsSatisfying(
+ point ->
+ point
+ .hasValue(11)
+ .hasAttribute(
+ AttributeKey.stringKey("pool"), "heap_pool")
+ .hasAttribute(AttributeKey.stringKey("type"), "heap"),
+ point ->
+ point
+ .hasValue(15)
+ .hasAttribute(
+ AttributeKey.stringKey("pool"), "non_heap_pool")
+ .hasAttribute(
+ AttributeKey.stringKey("type"), "non_heap")))));
+ testing.waitAndAssertMetrics(
+ "io.opentelemetry.runtime-metrics",
+ "process.runtime.jvm.memory.committed",
+ metrics ->
+ metrics.anySatisfy(
+ metricData ->
+ assertThat(metricData)
+ .hasInstrumentationScope(EXPECTED_SCOPE)
+ .hasDescription("Measure of memory committed")
+ .hasUnit("By")
+ .hasLongSumSatisfying(
+ sum ->
+ sum.hasPointsSatisfying(
+ point ->
+ point
+ .hasValue(12)
+ .hasAttribute(
+ AttributeKey.stringKey("pool"), "heap_pool")
+ .hasAttribute(AttributeKey.stringKey("type"), "heap"),
+ point ->
+ point
+ .hasValue(16)
+ .hasAttribute(
+ AttributeKey.stringKey("pool"), "non_heap_pool")
+ .hasAttribute(
+ AttributeKey.stringKey("type"), "non_heap")))));
+ testing.waitAndAssertMetrics(
+ "io.opentelemetry.runtime-metrics",
+ "process.runtime.jvm.memory.limit",
+ metrics ->
+ metrics.anySatisfy(
+ metricData ->
+ assertThat(metricData)
+ .hasInstrumentationScope(EXPECTED_SCOPE)
+ .hasDescription("Measure of max obtainable memory")
+ .hasUnit("By")
+ .hasLongSumSatisfying(
+ sum ->
+ sum.hasPointsSatisfying(
+ point ->
+ point
+ .hasValue(13)
+ .hasAttribute(
+ AttributeKey.stringKey("pool"), "heap_pool")
+ .hasAttribute(AttributeKey.stringKey("type"), "heap"),
+ point ->
+ point
+ .hasValue(17)
+ .hasAttribute(
+ AttributeKey.stringKey("pool"), "non_heap_pool")
+ .hasAttribute(
+ AttributeKey.stringKey("type"), "non_heap")))));
+ }
+
@Test
void callback_Records() {
when(heapPoolUsage.getUsed()).thenReturn(1L);

ADD
instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ScopeUtil.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.runtimemetrics;
+
+import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties;
+import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
+import java.util.Optional;
+
+class ScopeUtil {
+
+ static final InstrumentationScopeInfo EXPECTED_SCOPE =
+ InstrumentationScopeInfo.builder("io.opentelemetry.runtime-metrics")
+ .setVersion(
+ Optional.ofNullable(
+ EmbeddedInstrumentationProperties.findVersion(
+ "io.opentelemetry.runtime-metrics"))
+ .orElseThrow(
+ () -> new IllegalStateException("Unable to find instrumentation version")))
+ .build();
+
+ private ScopeUtil() {}
+}

MODIFY
instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ThreadsTest.java
instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/ThreadsTest.java
package io.opentelemetry.instrumentation.runtimemetrics;
+import static io.opentelemetry.instrumentation.runtimemetrics.ScopeUtil.EXPECTED_SCOPE;
import static io.opentelemetry.instrumentation.runtimemetrics.Threads.DAEMON;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
@@ -41,6 +42,7 @@ class ThreadsTest {
metrics.anySatisfy(
metricData ->
assertThat(metricData)
+ .hasInstrumentationScope(EXPECTED_SCOPE)
.hasDescription("Number of executing threads")
.hasUnit("1")
.hasLongSumSatisfying(

Add meter version to runtime metrics (#6874)

Runtime metrics doesn't include the meter version. This adds it from the
utility method in the instrumentation-api
`EmbeddedInstrumentationProperties.findVersion`. I know I can read the
properties file for this module, but its repetitive to implement that in
many places.