From 638ae4470ffc22499032775b318592db35293f50 Mon Sep 17 00:00:00 2001
From: jack-berg <34418638+jack-berg@users.noreply.github.com>
Date: Thu, 31 Mar 2022 11:56:58 -0500
Subject: [PATCH] Update runtime memory metrics to reflect semantic conventions
(#5718)
* Update runtime memory metrics to reflect semantic conventions
* Fix SpringBootSmokeTest
* Fix PrometheusSmokeTest
---
.../runtimemetrics/RuntimeMetricsTest.groovy | 6 +-
.../runtimemetrics/MemoryPools.java | 172 +++++++-----------
.../runtimemetrics/MemoryPoolsTest.java | 91 +++++----
.../smoketest/PrometheusSmokeTest.groovy | 2 +-
.../smoketest/SpringBootSmokeTest.groovy | 6 +-
5 files changed, 129 insertions(+), 148 deletions(-)
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 a3aa6c676d4e..4e66df5e090a 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
@@ -18,8 +18,10 @@ class RuntimeMetricsTest extends AgentInstrumentationSpecification {
conditions.eventually {
assert getMetrics().any { it.name == "runtime.jvm.gc.time" }
assert getMetrics().any { it.name == "runtime.jvm.gc.count" }
- assert getMetrics().any { it.name == "runtime.jvm.memory.area" }
- assert getMetrics().any { it.name == "runtime.jvm.memory.pool" }
+ assert getMetrics().any { it.name == "process.runtime.jvm.memory.init" }
+ 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" }
}
}
}
diff --git a/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPools.java b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPools.java
index 77a2fe771cc4..35f040b08b3a 100644
--- a/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPools.java
+++ b/instrumentation/runtime-metrics/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPools.java
@@ -12,14 +12,16 @@
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.ObservableLongMeasurement;
import java.lang.management.ManagementFactory;
-import java.lang.management.MemoryMXBean;
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 areas.
+ * Registers measurements that generate metrics about JVM memory pools.
*
*
Example usage:
*
@@ -30,132 +32,94 @@
*
Example metrics being exported: Component
*
*
- * runtime.jvm.memory.area{type="used",area="heap"} 2000000
- * runtime.jvm.memory.area{type="committed",area="non_heap"} 200000
- * runtime.jvm.memory.area{type="used",pool="PS Eden Space"} 2000
+ * process.runtime.jvm.memory.init{type="heap",pool="G1 Eden Space"} 1000000
+ * process.runtime.jvm.memory.usage{type="heap",pool="G1 Eden Space"} 2500000
+ * process.runtime.jvm.memory.committed{type="heap",pool="G1 Eden Space"} 3000000
+ * process.runtime.jvm.memory.max{type="heap",pool="G1 Eden Space"} 4000000
+ * process.runtime.jvm.memory.init{type="non_heap",pool="Metaspace"} 200
+ * process.runtime.jvm.memory.usage{type="non_heap",pool="Metaspace"} 400
+ * process.runtime.jvm.memory.committed{type="non_heap",pool="Metaspace"} 500
*
*/
public final class MemoryPools {
- // Visible for testing
- static final AttributeKey TYPE_KEY = AttributeKey.stringKey("type");
- // Visible for testing
- static final AttributeKey AREA_KEY = AttributeKey.stringKey("area");
+
+ private static final AttributeKey TYPE_KEY = AttributeKey.stringKey("type");
private static final AttributeKey POOL_KEY = AttributeKey.stringKey("pool");
- private static final String USED = "used";
- private static final String COMMITTED = "committed";
- private static final String MAX = "max";
private static final String HEAP = "heap";
private static final String NON_HEAP = "non_heap";
- private static final Attributes COMMITTED_HEAP =
- Attributes.of(TYPE_KEY, COMMITTED, AREA_KEY, HEAP);
- private static final Attributes USED_HEAP = Attributes.of(TYPE_KEY, USED, AREA_KEY, HEAP);
- private static final Attributes MAX_HEAP = Attributes.of(TYPE_KEY, MAX, AREA_KEY, HEAP);
-
- private static final Attributes COMMITTED_NON_HEAP =
- Attributes.of(TYPE_KEY, COMMITTED, AREA_KEY, NON_HEAP);
- private static final Attributes USED_NON_HEAP = Attributes.of(TYPE_KEY, USED, AREA_KEY, NON_HEAP);
- private static final Attributes MAX_NON_HEAP = Attributes.of(TYPE_KEY, MAX, AREA_KEY, NON_HEAP);
-
- /** Register only the "area" measurements. */
+ /**
+ * Register observers for java runtime memory metrics.
+ *
+ * @deprecated use {@link #registerObservers(OpenTelemetry openTelemetry)}
+ */
@Deprecated
- public static void registerMemoryAreaObservers() {
- registerMemoryPoolObservers(GlobalOpenTelemetry.get());
+ public static void registerObservers() {
+ registerObservers(GlobalOpenTelemetry.get());
}
- public static void registerMemoryAreaObservers(OpenTelemetry openTelemetry) {
- MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
- Meter meter = openTelemetry.getMeterProvider().get(MemoryPools.class.getName());
+ /** Register observers for java runtime memory metrics. */
+ public static void registerObservers(OpenTelemetry openTelemetry) {
+ List poolBeans = ManagementFactory.getMemoryPoolMXBeans();
+ Meter meter = openTelemetry.getMeter("io.opentelemetry.runtime-metrics");
+
meter
- .upDownCounterBuilder("runtime.jvm.memory.area")
- .setDescription("Bytes of a given JVM memory area.")
+ .upDownCounterBuilder("process.runtime.jvm.memory.usage")
+ .setDescription("Measure of memory used")
.setUnit("By")
- .buildWithCallback(
- resultLongObserver -> {
- recordHeap(resultLongObserver, memoryBean.getHeapMemoryUsage());
- recordNonHeap(resultLongObserver, memoryBean.getNonHeapMemoryUsage());
- });
- }
+ .buildWithCallback(callback(poolBeans, MemoryUsage::getUsed));
- /** Register only the "pool" measurements. */
- @Deprecated
- public static void registerMemoryPoolObservers() {
- registerMemoryPoolObservers(GlobalOpenTelemetry.get());
- }
-
- public static void registerMemoryPoolObservers(OpenTelemetry openTelemetry) {
- List poolBeans = ManagementFactory.getMemoryPoolMXBeans();
- Meter meter = openTelemetry.getMeterProvider().get(MemoryPools.class.getName());
- List usedLabelSets = new ArrayList<>(poolBeans.size());
- List committedLabelSets = new ArrayList<>(poolBeans.size());
- List maxLabelSets = new ArrayList<>(poolBeans.size());
- for (MemoryPoolMXBean pool : poolBeans) {
- usedLabelSets.add(Attributes.of(TYPE_KEY, USED, POOL_KEY, pool.getName()));
- committedLabelSets.add(Attributes.of(TYPE_KEY, COMMITTED, POOL_KEY, pool.getName()));
- maxLabelSets.add(Attributes.of(TYPE_KEY, MAX, POOL_KEY, pool.getName()));
- }
meter
- .upDownCounterBuilder("runtime.jvm.memory.pool")
- .setDescription("Bytes of a given JVM memory pool.")
+ .upDownCounterBuilder("process.runtime.jvm.memory.init")
+ .setDescription("Measure of initial memory requested")
.setUnit("By")
- .buildWithCallback(
- resultLongObserver -> {
- for (int i = 0; i < poolBeans.size(); i++) {
- MemoryUsage poolUsage = poolBeans.get(i).getUsage();
- if (poolUsage != null) {
- record(
- resultLongObserver,
- poolUsage,
- usedLabelSets.get(i),
- committedLabelSets.get(i),
- maxLabelSets.get(i));
- }
- }
- });
- }
+ .buildWithCallback(callback(poolBeans, MemoryUsage::getInit));
- /**
- * Register all measurements provided by this module.
- *
- * @deprecated use {@link #registerObservers(OpenTelemetry openTelemetry)}
- */
- @Deprecated
- public static void registerObservers() {
- registerMemoryAreaObservers();
- registerMemoryPoolObservers();
- }
+ meter
+ .upDownCounterBuilder("process.runtime.jvm.memory.committed")
+ .setDescription("Measure of memory committed")
+ .setUnit("By")
+ .buildWithCallback(callback(poolBeans, MemoryUsage::getCommitted));
- /** Register all measurements provided by this module. */
- public static void registerObservers(OpenTelemetry openTelemetry) {
- registerMemoryAreaObservers(openTelemetry);
- registerMemoryPoolObservers(openTelemetry);
+ meter
+ .upDownCounterBuilder("process.runtime.jvm.memory.max")
+ .setDescription("Measure of max obtainable memory")
+ .setUnit("By")
+ .buildWithCallback(callback(poolBeans, MemoryUsage::getMax));
}
- static void recordHeap(ObservableLongMeasurement measurement, MemoryUsage usage) {
- record(measurement, usage, USED_HEAP, COMMITTED_HEAP, MAX_HEAP);
- }
+ // Visible for testing
+ static Consumer callback(
+ List poolBeans, Function extractor) {
+ List attributeSets = new ArrayList<>(poolBeans.size());
+ for (MemoryPoolMXBean pool : poolBeans) {
+ attributeSets.add(
+ Attributes.builder()
+ .put(POOL_KEY, pool.getName())
+ .put(TYPE_KEY, memoryType(pool.getType()))
+ .build());
+ }
- static void recordNonHeap(ObservableLongMeasurement measurement, MemoryUsage usage) {
- record(measurement, usage, USED_NON_HEAP, COMMITTED_NON_HEAP, MAX_NON_HEAP);
+ return measurement -> {
+ for (int i = 0; i < poolBeans.size(); i++) {
+ Attributes attributes = attributeSets.get(i);
+ long value = extractor.apply(poolBeans.get(i).getUsage());
+ if (value != -1) {
+ measurement.record(value, attributes);
+ }
+ }
+ };
}
- private static void record(
- ObservableLongMeasurement measurement,
- MemoryUsage usage,
- Attributes usedAttributes,
- Attributes committedAttributes,
- Attributes maxAttributes) {
- // TODO: Decide if init is needed or not. It is a constant that can be queried once on startup.
- // if (usage.getInit() != -1) {
- // measurement.record(usage.getInit(), ...);
- // }
- measurement.record(usage.getUsed(), usedAttributes);
- measurement.record(usage.getCommitted(), committedAttributes);
- // TODO: Decide if max is needed or not. It is a constant that can be queried once on startup.
- if (usage.getMax() != -1) {
- measurement.record(usage.getMax(), maxAttributes);
+ private static String memoryType(MemoryType memoryType) {
+ switch (memoryType) {
+ case HEAP:
+ return HEAP;
+ case NON_HEAP:
+ return NON_HEAP;
}
+ return "unknown";
}
private MemoryPools() {}
diff --git a/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPoolsTest.java b/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPoolsTest.java
index fa498126194a..cac71df834ec 100644
--- a/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPoolsTest.java
+++ b/instrumentation/runtime-metrics/library/src/test/java/io/opentelemetry/instrumentation/runtimemetrics/MemoryPoolsTest.java
@@ -5,64 +5,77 @@
package io.opentelemetry.instrumentation.runtimemetrics;
-import static org.mockito.Mockito.mock;
+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.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.ObservableLongMeasurement;
+import java.lang.management.MemoryPoolMXBean;
+import java.lang.management.MemoryType;
import java.lang.management.MemoryUsage;
+import java.util.Arrays;
+import java.util.List;
+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.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+@ExtendWith(MockitoExtension.class)
class MemoryPoolsTest {
- @Test
- void observeHeap() {
- ObservableLongMeasurement measurement = mock(ObservableLongMeasurement.class);
- MemoryPools.recordHeap(measurement, new MemoryUsage(-1, 1, 2, 3));
- verify(measurement)
- .record(1, Attributes.of(MemoryPools.TYPE_KEY, "used", MemoryPools.AREA_KEY, "heap"));
- verify(measurement)
- .record(2, Attributes.of(MemoryPools.TYPE_KEY, "committed", MemoryPools.AREA_KEY, "heap"));
- verify(measurement)
- .record(3, Attributes.of(MemoryPools.TYPE_KEY, "max", MemoryPools.AREA_KEY, "heap"));
- verifyNoMoreInteractions(measurement);
- }
+ @Spy private ObservableLongMeasurement measurement;
- @Test
- void observeHeapNoMax() {
- ObservableLongMeasurement measurement = mock(ObservableLongMeasurement.class);
- MemoryPools.recordHeap(measurement, new MemoryUsage(-1, 1, 2, -1));
- verify(measurement)
- .record(1, Attributes.of(MemoryPools.TYPE_KEY, "used", MemoryPools.AREA_KEY, "heap"));
- verify(measurement)
- .record(2, Attributes.of(MemoryPools.TYPE_KEY, "committed", MemoryPools.AREA_KEY, "heap"));
- verifyNoMoreInteractions(measurement);
+ @Mock private MemoryPoolMXBean heapPoolBean;
+ @Mock private MemoryPoolMXBean nonHeapPoolBean;
+
+ @Mock private MemoryUsage heapPoolUsage;
+ @Mock private MemoryUsage nonHeapUsage;
+
+ private List beans;
+
+ @BeforeEach
+ void setup() {
+ when(heapPoolBean.getName()).thenReturn("heap_pool");
+ when(heapPoolBean.getType()).thenReturn(MemoryType.HEAP);
+ when(heapPoolBean.getUsage()).thenReturn(heapPoolUsage);
+ when(nonHeapPoolBean.getName()).thenReturn("non_heap_pool");
+ when(nonHeapPoolBean.getType()).thenReturn(MemoryType.NON_HEAP);
+ when(nonHeapPoolBean.getUsage()).thenReturn(nonHeapUsage);
+ beans = Arrays.asList(heapPoolBean, nonHeapPoolBean);
}
@Test
- void observeNonHeap() {
- ObservableLongMeasurement measurement = mock(ObservableLongMeasurement.class);
- MemoryPools.recordNonHeap(measurement, new MemoryUsage(-1, 4, 5, 6));
+ void callback_Records() {
+ when(heapPoolUsage.getUsed()).thenReturn(1L);
+ when(nonHeapUsage.getUsed()).thenReturn(2L);
+
+ Consumer callback =
+ MemoryPools.callback(beans, MemoryUsage::getUsed);
+ callback.accept(measurement);
+
verify(measurement)
- .record(4, Attributes.of(MemoryPools.TYPE_KEY, "used", MemoryPools.AREA_KEY, "non_heap"));
+ .record(1, Attributes.builder().put("pool", "heap_pool").put("type", "heap").build());
verify(measurement)
.record(
- 5, Attributes.of(MemoryPools.TYPE_KEY, "committed", MemoryPools.AREA_KEY, "non_heap"));
- verify(measurement)
- .record(6, Attributes.of(MemoryPools.TYPE_KEY, "max", MemoryPools.AREA_KEY, "non_heap"));
- verifyNoMoreInteractions(measurement);
+ 2, Attributes.builder().put("pool", "non_heap_pool").put("type", "non_heap").build());
}
@Test
- void observeNonHeapNoMax() {
- ObservableLongMeasurement measurement = mock(ObservableLongMeasurement.class);
- MemoryPools.recordNonHeap(measurement, new MemoryUsage(-1, 4, 5, -1));
- verify(measurement)
- .record(4, Attributes.of(MemoryPools.TYPE_KEY, "used", MemoryPools.AREA_KEY, "non_heap"));
+ void callback_SkipRecord() {
+ when(heapPoolUsage.getMax()).thenReturn(1L);
+ when(nonHeapUsage.getMax()).thenReturn(-1L);
+
+ Consumer callback = MemoryPools.callback(beans, MemoryUsage::getMax);
+ callback.accept(measurement);
+
verify(measurement)
- .record(
- 5, Attributes.of(MemoryPools.TYPE_KEY, "committed", MemoryPools.AREA_KEY, "non_heap"));
- verifyNoMoreInteractions(measurement);
+ .record(1, Attributes.builder().put("pool", "heap_pool").put("type", "heap").build());
+ verify(measurement, never()).record(eq(-1), any());
}
}
diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/PrometheusSmokeTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/PrometheusSmokeTest.groovy
index d9c1d50e7cc4..ce86e16160db 100644
--- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/PrometheusSmokeTest.groovy
+++ b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/PrometheusSmokeTest.groovy
@@ -44,7 +44,7 @@ class PrometheusSmokeTest extends SmokeTest {
def prometheusClient = WebClient.of("h1c://localhost:${containerManager.getTargetMappedPort(9090)}")
def prometheusData = prometheusClient.get("/").aggregate().join().contentUtf8()
- prometheusData.contains("runtime_jvm_memory_pool")
+ prometheusData.contains("process_runtime_jvm_memory_usage")
cleanup:
stopTarget()
diff --git a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SpringBootSmokeTest.groovy b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SpringBootSmokeTest.groovy
index 73235aa0a73b..bf8f9199d6e1 100644
--- a/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SpringBootSmokeTest.groovy
+++ b/smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SpringBootSmokeTest.groovy
@@ -79,8 +79,10 @@ class SpringBootSmokeTest extends SmokeTest {
def metrics = new MetricsInspector(waitForMetrics())
metrics.hasMetricsNamed("runtime.jvm.gc.time")
metrics.hasMetricsNamed("runtime.jvm.gc.count")
- metrics.hasMetricsNamed("runtime.jvm.memory.area")
- metrics.hasMetricsNamed("runtime.jvm.memory.pool")
+ metrics.hasMetricsNamed("process.runtime.jvm.memory.init")
+ metrics.hasMetricsNamed("process.runtime.jvm.memory.usage")
+ metrics.hasMetricsNamed("process.runtime.jvm.memory.committed")
+ metrics.hasMetricsNamed("process.runtime.jvm.memory.max")
cleanup:
stopTarget()