From dc3199aac440dee8e31a13526e9a6eeb44b8f8ba Mon Sep 17 00:00:00 2001 From: Mateusz Rzeszutek Date: Tue, 13 Dec 2022 12:20:14 +0100 Subject: [PATCH] Remove some metrics that are already covered in upstream (#1005) * Remove some metrics that are already covered in upstream * Fix test * removed some more stuff --- .../jvmmetrics/JvmMetricsInstaller.java | 23 +- .../jvmmetrics/otel/JvmMemory.java | 33 -- .../jvmmetrics/otel/OtelJvmGcMetrics.java | 314 ------------------ .../otel/OtelJvmHeapPressureMetrics.java | 150 --------- .../jvmmetrics/otel/OtelJvmThreadMetrics.java | 23 +- .../jvmmetrics/OtelJvmMetricsTest.java | 18 +- 6 files changed, 36 insertions(+), 525 deletions(-) delete mode 100644 instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/otel/OtelJvmGcMetrics.java delete mode 100644 instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/otel/OtelJvmHeapPressureMetrics.java diff --git a/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/JvmMetricsInstaller.java b/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/JvmMetricsInstaller.java index 8c5d4c36d..a9e0d1036 100644 --- a/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/JvmMetricsInstaller.java +++ b/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/JvmMetricsInstaller.java @@ -25,8 +25,6 @@ import com.splunk.opentelemetry.instrumentation.jvmmetrics.micrometer.MicrometerGcMemoryMetrics; import com.splunk.opentelemetry.instrumentation.jvmmetrics.otel.OtelAllocatedMemoryMetrics; import com.splunk.opentelemetry.instrumentation.jvmmetrics.otel.OtelGcMemoryMetrics; -import com.splunk.opentelemetry.instrumentation.jvmmetrics.otel.OtelJvmGcMetrics; -import com.splunk.opentelemetry.instrumentation.jvmmetrics.otel.OtelJvmHeapPressureMetrics; import com.splunk.opentelemetry.instrumentation.jvmmetrics.otel.OtelJvmThreadMetrics; import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics; @@ -59,8 +57,25 @@ public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetr new JvmThreadMetrics().bindTo(Metrics.globalRegistry); } if (useOtelMetrics(metricsImplementation)) { - new OtelJvmGcMetrics().install(); - new OtelJvmHeapPressureMetrics().install(); + // gc metrics were removed: + // runtime.jvm.gc.concurrent.phase.time is replaced by OTel + // process.runtime.jvm.gc.duration{gc=} + // runtime.jvm.gc.pause is replaced by OTel + // process.runtime.jvm.gc.duration{gc!=} + // runtime.jvm.gc.max.data.size is replaced by OTel + // process.runtime.jvm.memory.limit{pool=} + // runtime.jvm.gc.live.data.size is replaced by OTel + // process.runtime.jvm.memory.usage_after_last_gc{pool=} + // runtime.jvm.gc.memory.allocated is replaced by memory profiling metric + // process.runtime.jvm.memory.allocated + // runtime.jvm.gc.memory.promoted is removed with no direct replacement + + // heap pressure metrics were removed: + // runtime.jvm.memory.usage.after.gc is replaced by OTel + // process.runtime.jvm.memory.usage_after_last_gc{pool=,type=heap} / + // process.runtime.jvm.memory.limit{pool=,type=heap} + // runtime.jvm.gc.overhead is something that should to done in a dashboard, not here + new OtelJvmThreadMetrics().install(); } diff --git a/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/otel/JvmMemory.java b/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/otel/JvmMemory.java index 2d48f6591..455ae62e4 100644 --- a/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/otel/JvmMemory.java +++ b/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/otel/JvmMemory.java @@ -33,25 +33,14 @@ package com.splunk.opentelemetry.instrumentation.jvmmetrics.otel; -import java.lang.management.ManagementFactory; import java.lang.management.MemoryPoolMXBean; import java.lang.management.MemoryType; -import java.lang.management.MemoryUsage; -import java.util.function.ToLongFunction; -import java.util.stream.Stream; -import javax.annotation.Nullable; /** This class is copied from micrometer. */ class JvmMemory { private JvmMemory() {} - static Stream getLongLivedHeapPools() { - return ManagementFactory.getMemoryPoolMXBeans().stream() - .filter(JvmMemory::isHeap) - .filter(mem -> isLongLivedPool(mem.getName())); - } - static boolean isConcurrentPhase(String cause, String name) { return "No GC".equals(cause) || "Shenandoah Cycles".equals(name) || "ZGC Cycles".equals(name); } @@ -84,26 +73,4 @@ static boolean isLongLivedPool(String name) { static boolean isHeap(MemoryPoolMXBean memoryPoolBean) { return MemoryType.HEAP.equals(memoryPoolBean.getType()); } - - static double getUsageValue( - MemoryPoolMXBean memoryPoolMXBean, ToLongFunction getter) { - MemoryUsage usage = getUsage(memoryPoolMXBean); - if (usage == null) { - return Double.NaN; - } - return getter.applyAsLong(usage); - } - - @Nullable - private static MemoryUsage getUsage(MemoryPoolMXBean memoryPoolMXBean) { - try { - return memoryPoolMXBean.getUsage(); - } catch (InternalError e) { - // Defensive for potential InternalError with some specific JVM options. Based - // on its Javadoc, - // MemoryPoolMXBean.getUsage() should return null, not throwing InternalError, - // so it seems to be a JVM bug. - return null; - } - } } diff --git a/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/otel/OtelJvmGcMetrics.java b/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/otel/OtelJvmGcMetrics.java deleted file mode 100644 index 25a2e720b..000000000 --- a/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/otel/OtelJvmGcMetrics.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Includes work from: -/* - * Copyright 2019 VMware, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.splunk.opentelemetry.instrumentation.jvmmetrics.otel; - -import static com.splunk.opentelemetry.instrumentation.jvmmetrics.otel.JvmMemory.getLongLivedHeapPools; -import static com.splunk.opentelemetry.instrumentation.jvmmetrics.otel.JvmMemory.getUsageValue; -import static com.splunk.opentelemetry.instrumentation.jvmmetrics.otel.JvmMemory.isAllocationPool; -import static com.splunk.opentelemetry.instrumentation.jvmmetrics.otel.JvmMemory.isConcurrentPhase; -import static com.splunk.opentelemetry.instrumentation.jvmmetrics.otel.JvmMemory.isLongLivedPool; -import static io.opentelemetry.api.common.AttributeKey.stringKey; - -import com.sun.management.GarbageCollectionNotificationInfo; -import com.sun.management.GcInfo; -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.metrics.LongCounter; -import io.opentelemetry.api.metrics.LongHistogram; -import io.opentelemetry.api.metrics.Meter; -import java.lang.management.GarbageCollectorMXBean; -import java.lang.management.ManagementFactory; -import java.lang.management.MemoryPoolMXBean; -import java.lang.management.MemoryUsage; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicLong; -import java.util.logging.Logger; -import javax.management.Notification; -import javax.management.NotificationEmitter; -import javax.management.NotificationListener; -import javax.management.openmbean.CompositeData; - -public class OtelJvmGcMetrics { - private static final Logger logger = Logger.getLogger(OtelJvmGcMetrics.class.getName()); - - private static final AttributeKey ACTION = stringKey("action"); - private static final AttributeKey CAUSE = stringKey("cause"); - - private final boolean managementExtensionsPresent = isManagementExtensionsPresent(); - private final boolean isGenerationalGc = isGenerationalGcConfigured(); - private String allocationPoolName; - private final Set longLivedPoolNames = new HashSet<>(); - - private LongCounter allocatedBytes; - private LongCounter promotedBytes; - private AtomicLong allocationPoolSizeAfter; - private AtomicLong liveDataSize; - private AtomicLong maxDataSize; - - public OtelJvmGcMetrics() { - for (MemoryPoolMXBean mbean : ManagementFactory.getMemoryPoolMXBeans()) { - String name = mbean.getName(); - if (isAllocationPool(name)) { - allocationPoolName = name; - } - if (isLongLivedPool(name)) { - longLivedPoolNames.add(name); - } - } - } - - public void install() { - if (!this.managementExtensionsPresent) { - return; - } - - Meter meter = OtelMeterProvider.get(); - - GcMetricsNotificationListener gcNotificationListener = new GcMetricsNotificationListener(meter); - - double maxLongLivedPoolBytes = - getLongLivedHeapPools().mapToDouble(mem -> getUsageValue(mem, MemoryUsage::getMax)).sum(); - - maxDataSize = new AtomicLong((long) maxLongLivedPoolBytes); - meter - .gaugeBuilder("runtime.jvm.gc.max.data.size") - .setUnit("bytes") - .setDescription("Max size of long-lived heap memory pool.") - .buildWithCallback(measurement -> measurement.record(maxDataSize.get())); - - liveDataSize = new AtomicLong(); - meter - .gaugeBuilder("runtime.jvm.gc.live.data.size") - .setUnit("bytes") - .setDescription("Size of long-lived heap memory pool after reclamation.") - .buildWithCallback(measurement -> measurement.record(liveDataSize.get())); - - allocatedBytes = - meter - .counterBuilder("runtime.jvm.gc.memory.allocated") - .setUnit("bytes") - .setDescription("Size of long-lived heap memory pool after reclamation.") - .build(); - - if (isGenerationalGc) { - promotedBytes = - meter - .counterBuilder("runtime.jvm.gc.memory.promoted") - .setUnit("bytes") - .setDescription( - "Count of positive increases in the size of the old generation memory pool before GC to after GC.") - .build(); - } - - allocationPoolSizeAfter = new AtomicLong(0L); - - for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans()) { - if (!(gcBean instanceof NotificationEmitter)) { - continue; - } - NotificationEmitter notificationEmitter = (NotificationEmitter) gcBean; - notificationEmitter.addNotificationListener( - gcNotificationListener, - notification -> - notification - .getType() - .equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION), - null); - } - } - - private class GcMetricsNotificationListener implements NotificationListener { - - private final LongHistogram concurrentPhaseHistogram; - private final LongHistogram pauseHistogram; - - GcMetricsNotificationListener(Meter meter) { - concurrentPhaseHistogram = - meter - .histogramBuilder("runtime.jvm.gc.concurrent.phase.time") - .ofLongs() - .setUnit("ms") - .setDescription("Time spent in concurrent phase.") - .build(); - pauseHistogram = - meter - .histogramBuilder("runtime.jvm.gc.pause") - .ofLongs() - .setUnit("ms") - .setDescription("Time spent in GC pause.") - .build(); - } - - @Override - public void handleNotification(Notification notification, Object ref) { - CompositeData cd = (CompositeData) notification.getUserData(); - GarbageCollectionNotificationInfo notificationInfo = - GarbageCollectionNotificationInfo.from(cd); - - String gcCause = notificationInfo.getGcCause(); - String gcAction = notificationInfo.getGcAction(); - GcInfo gcInfo = notificationInfo.getGcInfo(); - long duration = gcInfo.getDuration(); - Attributes attributes = Attributes.of(ACTION, gcAction, CAUSE, gcCause); - if (isConcurrentPhase(gcCause, notificationInfo.getGcName())) { - concurrentPhaseHistogram.record(duration, attributes); - } else { - pauseHistogram.record(duration, attributes); - } - - final Map before = gcInfo.getMemoryUsageBeforeGc(); - final Map after = gcInfo.getMemoryUsageAfterGc(); - - countPoolSizeDelta(before, after); - - final long longLivedBefore = - longLivedPoolNames.stream().mapToLong(pool -> before.get(pool).getUsed()).sum(); - final long longLivedAfter = - longLivedPoolNames.stream().mapToLong(pool -> after.get(pool).getUsed()).sum(); - if (isGenerationalGc) { - final long delta = longLivedAfter - longLivedBefore; - if (delta > 0L) { - promotedBytes.add(delta); - } - } - - // Some GC implementations such as G1 can reduce the old gen size as part of a minor GC. To - // track the live data size we record the value if we see a reduction in the long-lived heap - // size or after a major/non-generational GC. - if (longLivedAfter < longLivedBefore - || shouldUpdateDataSizeMetrics(notificationInfo.getGcName())) { - liveDataSize.set(longLivedAfter); - maxDataSize.set( - longLivedPoolNames.stream().mapToLong(pool -> after.get(pool).getMax()).sum()); - } - } - - private void countPoolSizeDelta( - Map before, Map after) { - if (allocationPoolName == null) { - return; - } - final long beforeBytes = before.get(allocationPoolName).getUsed(); - final long afterBytes = after.get(allocationPoolName).getUsed(); - final long delta = beforeBytes - allocationPoolSizeAfter.get(); - allocationPoolSizeAfter.set(afterBytes); - if (delta > 0L) { - allocatedBytes.add(delta); - } - } - - private boolean shouldUpdateDataSizeMetrics(String gcName) { - return nonGenerationalGcShouldUpdateDataSize(gcName) || isMajorGenerationalGc(gcName); - } - - private boolean isMajorGenerationalGc(String gcName) { - return GcGenerationAge.fromGcName(gcName) == GcGenerationAge.OLD; - } - - private boolean nonGenerationalGcShouldUpdateDataSize(String gcName) { - return !isGenerationalGc - // Skip Shenandoah and ZGC gc notifications with the name Pauses due - // to missing memory pool size info - && !gcName.endsWith("Pauses"); - } - } - - private boolean isGenerationalGcConfigured() { - return ManagementFactory.getMemoryPoolMXBeans().stream() - .filter(JvmMemory::isHeap) - .map(MemoryPoolMXBean::getName) - .filter(name -> !name.contains("tenured")) - .count() - > 1; - } - - private static boolean isManagementExtensionsPresent() { - if (ManagementFactory.getMemoryPoolMXBeans().isEmpty()) { - // Substrate VM, for example, doesn't provide or support these beans (yet) - logger.warning( - "GC notifications will not be available because MemoryPoolMXBeans are not provided by the JVM"); - return false; - } - - try { - Class.forName( - "com.sun.management.GarbageCollectionNotificationInfo", - false, - MemoryPoolMXBean.class.getClassLoader()); - return true; - } catch (Throwable e) { - // We are operating in a JVM without access to this level of detail - logger.warning( - "GC notifications will not be available because " - + "com.sun.management.GarbageCollectionNotificationInfo is not present"); - return false; - } - } - - /** - * Generalization of which parts of the heap are considered "young" or "old" for multiple GC - * implementations - */ - enum GcGenerationAge { - OLD, - YOUNG, - UNKNOWN; - - private static final Map knownCollectors = - new HashMap() { - { - put("ConcurrentMarkSweep", OLD); - put("Copy", YOUNG); - put("G1 Old Generation", OLD); - put("G1 Young Generation", YOUNG); - put("MarkSweepCompact", OLD); - put("PS MarkSweep", OLD); - put("PS Scavenge", YOUNG); - put("ParNew", YOUNG); - put("global", OLD); - put("scavenge", YOUNG); - put("partial gc", YOUNG); - put("global garbage collect", OLD); - put("Epsilon", OLD); - } - }; - - static GcGenerationAge fromGcName(String gcName) { - return knownCollectors.getOrDefault(gcName, UNKNOWN); - } - } -} diff --git a/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/otel/OtelJvmHeapPressureMetrics.java b/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/otel/OtelJvmHeapPressureMetrics.java deleted file mode 100644 index 577eb49d9..000000000 --- a/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/otel/OtelJvmHeapPressureMetrics.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Includes work from: -/* - * Copyright 2019 VMware, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.splunk.opentelemetry.instrumentation.jvmmetrics.otel; - -import static io.opentelemetry.api.common.AttributeKey.stringKey; - -import com.sun.management.GarbageCollectionNotificationInfo; -import com.sun.management.GcInfo; -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.metrics.Meter; -import java.lang.management.GarbageCollectorMXBean; -import java.lang.management.ManagementFactory; -import java.lang.management.MemoryPoolMXBean; -import java.lang.management.MemoryUsage; -import java.time.Duration; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; -import javax.management.NotificationEmitter; -import javax.management.NotificationListener; -import javax.management.openmbean.CompositeData; - -public class OtelJvmHeapPressureMetrics { - private static final AttributeKey AREA = stringKey("area"); - private static final AttributeKey POOL = stringKey("pool"); - - private final long startOfMonitoring = System.nanoTime(); - private final Duration lookback; - private final TimeWindowSum gcPauseSum; - private final AtomicReference lastLongLivedPoolUsageAfterGc = new AtomicReference<>(0.0); - private final Set longLivedPoolNames; - - public OtelJvmHeapPressureMetrics() { - this(Duration.ofMinutes(5), Duration.ofMinutes(1)); - } - - private OtelJvmHeapPressureMetrics(Duration lookback, Duration testEvery) { - this.lookback = lookback; - this.gcPauseSum = - new TimeWindowSum((int) lookback.dividedBy(testEvery.toMillis()).toMillis(), testEvery); - - longLivedPoolNames = - JvmMemory.getLongLivedHeapPools() - .map(MemoryPoolMXBean::getName) - .collect(Collectors.toSet()); - - monitor(); - } - - public void install() { - Meter meter = OtelMeterProvider.get(); - - if (!longLivedPoolNames.isEmpty()) { - Attributes attributes = Attributes.of(AREA, "heap", POOL, "long-lived"); - - meter - .gaugeBuilder("runtime.jvm.memory.usage.after.gc") - .setUnit("percent") - .setDescription( - "The percentage of long-lived heap pool used after the last GC event, in the range [0..1].") - .buildWithCallback( - measurement -> measurement.record(lastLongLivedPoolUsageAfterGc.get(), attributes)); - } - - meter - .gaugeBuilder("runtime.jvm.gc.overhead") - .setUnit("percent") - .setDescription( - "An approximation of the percent of CPU time used by GC activities over the last lookback period or since monitoring began, whichever is shorter, in the range [0..1].") - .buildWithCallback( - measurement -> { - double overIntervalMillis = - Math.min(System.nanoTime() - startOfMonitoring, lookback.toNanos()) / 1e6; - measurement.record(gcPauseSum.poll() / overIntervalMillis); - }); - } - - private void monitor() { - for (GarbageCollectorMXBean mbean : ManagementFactory.getGarbageCollectorMXBeans()) { - if (!(mbean instanceof NotificationEmitter)) { - continue; - } - NotificationListener notificationListener = - (notification, ref) -> { - CompositeData cd = (CompositeData) notification.getUserData(); - GarbageCollectionNotificationInfo notificationInfo = - GarbageCollectionNotificationInfo.from(cd); - - String gcCause = notificationInfo.getGcCause(); - GcInfo gcInfo = notificationInfo.getGcInfo(); - long duration = gcInfo.getDuration(); - - if (!JvmMemory.isConcurrentPhase(gcCause, notificationInfo.getGcName())) { - gcPauseSum.record(duration); - } - - Map after = gcInfo.getMemoryUsageAfterGc(); - - if (!longLivedPoolNames.isEmpty()) { - final long usedAfter = - longLivedPoolNames.stream().mapToLong(pool -> after.get(pool).getUsed()).sum(); - double maxAfter = - longLivedPoolNames.stream().mapToLong(pool -> after.get(pool).getMax()).sum(); - lastLongLivedPoolUsageAfterGc.set(usedAfter / maxAfter); - } - }; - NotificationEmitter notificationEmitter = (NotificationEmitter) mbean; - notificationEmitter.addNotificationListener( - notificationListener, - notification -> - notification - .getType() - .equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION), - null); - } - } -} diff --git a/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/otel/OtelJvmThreadMetrics.java b/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/otel/OtelJvmThreadMetrics.java index 3435848ef..b155c166b 100644 --- a/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/otel/OtelJvmThreadMetrics.java +++ b/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/otel/OtelJvmThreadMetrics.java @@ -50,25 +50,10 @@ public void install() { Meter meter = OtelMeterProvider.get(); ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); - meter - .gaugeBuilder("runtime.jvm.threads.peak") - .setUnit("threads") - .setDescription( - "The peak live thread count since the Java virtual machine started or peak was reset.") - .buildWithCallback(measurement -> measurement.record(threadBean.getPeakThreadCount())); - - meter - .gaugeBuilder("runtime.jvm.threads.daemon") - .setUnit("threads") - .setDescription("The current number of live daemon threads.") - .buildWithCallback(measurement -> measurement.record(threadBean.getDaemonThreadCount())); - - meter - .gaugeBuilder("runtime.jvm.threads.live") - .setUnit("threads") - .setDescription( - "The current number of live threads including both daemon and non-daemon threads.") - .buildWithCallback(measurement -> measurement.record(threadBean.getThreadCount())); + // runtime.jvm.threads.peak was removed; easy to replace with + // max(process.runtime.jvm.threads.count) + // runtime.jvm.threads.daemon is replaced by OTel process.runtime.jvm.threads.count{daemon=true} + // runtime.jvm.threads.live is replaced by OTel process.runtime.jvm.threads.count{daemon=false} try { threadBean.getAllThreadIds(); diff --git a/instrumentation/jvm-metrics/src/test/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/OtelJvmMetricsTest.java b/instrumentation/jvm-metrics/src/test/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/OtelJvmMetricsTest.java index 1d1947fae..64c92dcfc 100644 --- a/instrumentation/jvm-metrics/src/test/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/OtelJvmMetricsTest.java +++ b/instrumentation/jvm-metrics/src/test/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/OtelJvmMetricsTest.java @@ -18,6 +18,8 @@ import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.ArrayList; +import java.util.List; import org.assertj.core.api.AbstractIterableAssert; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -32,12 +34,18 @@ class OtelJvmMetricsTest { @Test void shouldRegisterOtelJvmMeters() { - // GC metrics - assertOtelMetricPresent("runtime.jvm.gc.memory.allocated"); - // GC pressure metrics - assertOtelMetricPresent("runtime.jvm.gc.overhead"); + // exercise the GC for a bit + { + List garbage = new ArrayList<>(); + for (int i = 0; i < 10_000; ++i) { + garbage.add(new Long[i]); + } + garbage.clear(); + } + System.gc(); + // thread metrics - assertOtelMetricPresent("runtime.jvm.threads.peak"); + assertOtelMetricPresent("runtime.jvm.threads.states"); // allocated memory metrics assertOtelMetricPresent(AllocatedMemoryMetrics.METRIC_NAME); // Our custom GC metrics