Enable stable JVM semconv by default (#9963)

Co-authored-by: Mateusz Rzeszutek <mrzeszutek@splunk.com>
This commit is contained in:
Mateusz Rzeszutek 2023-12-14 18:20:27 +01:00 committed by GitHub
parent 5522a23a82
commit bdeec9fc0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 206 additions and 1471 deletions

View File

@ -1,52 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.api.internal;
import static java.util.Arrays.asList;
import java.util.HashSet;
import java.util.Set;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public final class SemconvStability {
private static final boolean emitOldJvmSemconv;
private static final boolean emitStableJvmSemconv;
static {
boolean oldJvm = true;
boolean stableJvm = false;
String value = ConfigPropertiesUtil.getString("otel.semconv-stability.opt-in");
if (value != null) {
Set<String> values = new HashSet<>(asList(value.split(",")));
if (values.contains("jvm")) {
oldJvm = false;
stableJvm = true;
}
if (values.contains("jvm/dup")) {
oldJvm = true;
stableJvm = true;
}
}
emitOldJvmSemconv = oldJvm;
emitStableJvmSemconv = stableJvm;
}
public static boolean emitOldJvmSemconv() {
return emitOldJvmSemconv;
}
public static boolean emitStableJvmSemconv() {
return emitStableJvmSemconv;
}
private SemconvStability() {}
}

View File

@ -22,21 +22,17 @@ class JmxRuntimeMetricsTest {
testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-telemetry-java8",
metric -> metric.hasName("process.runtime.jvm.classes.loaded"),
metric -> metric.hasName("process.runtime.jvm.classes.unloaded"),
metric -> metric.hasName("process.runtime.jvm.classes.current_loaded"),
metric -> metric.hasName("process.runtime.jvm.system.cpu.load_1m"),
metric -> metric.hasName("process.runtime.jvm.system.cpu.utilization"),
metric -> metric.hasName("process.runtime.jvm.cpu.utilization"),
metric -> metric.hasName("process.runtime.jvm.gc.duration"),
metric -> metric.hasName("process.runtime.jvm.memory.init"),
metric -> metric.hasName("process.runtime.jvm.memory.usage"),
metric -> metric.hasName("process.runtime.jvm.memory.committed"),
metric -> metric.hasName("process.runtime.jvm.memory.limit"),
metric -> metric.hasName("process.runtime.jvm.memory.usage_after_last_gc"),
metric -> metric.hasName("process.runtime.jvm.threads.count"),
metric -> metric.hasName("process.runtime.jvm.buffer.limit"),
metric -> metric.hasName("process.runtime.jvm.buffer.count"),
metric -> metric.hasName("process.runtime.jvm.buffer.usage"));
metric -> metric.hasName("jvm.class.loaded"),
metric -> metric.hasName("jvm.class.unloaded"),
metric -> metric.hasName("jvm.class.count"),
metric -> metric.hasName("jvm.cpu.time"),
metric -> metric.hasName("jvm.cpu.count"),
metric -> metric.hasName("jvm.cpu.recent_utilization"),
metric -> metric.hasName("jvm.gc.duration"),
metric -> metric.hasName("jvm.memory.used"),
metric -> metric.hasName("jvm.memory.committed"),
metric -> metric.hasName("jvm.memory.limit"),
metric -> metric.hasName("jvm.memory.used_after_last_gc"),
metric -> metric.hasName("jvm.thread.count"));
}
}

View File

@ -7,7 +7,6 @@ package io.opentelemetry.instrumentation.runtimemetrics.java17;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.runtimemetrics.java8.BufferPools;
import io.opentelemetry.instrumentation.runtimemetrics.java8.Classes;
import io.opentelemetry.instrumentation.runtimemetrics.java8.Cpu;
import io.opentelemetry.instrumentation.runtimemetrics.java8.GarbageCollector;
@ -106,7 +105,6 @@ public final class RuntimeMetricsBuilder {
try {
// Set up metrics gathered by JMX
List<AutoCloseable> observables = new ArrayList<>();
observables.addAll(BufferPools.registerObservers(openTelemetry));
observables.addAll(Classes.registerObservers(openTelemetry));
observables.addAll(Cpu.registerObservers(openTelemetry));
observables.addAll(GarbageCollector.registerObservers(openTelemetry));

View File

@ -8,7 +8,6 @@ package io.opentelemetry.instrumentation.javaagent.runtimemetrics.java8;
import com.google.auto.service.AutoService;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.runtimemetrics.java8.BufferPools;
import io.opentelemetry.instrumentation.runtimemetrics.java8.Classes;
import io.opentelemetry.instrumentation.runtimemetrics.java8.Cpu;
import io.opentelemetry.instrumentation.runtimemetrics.java8.GarbageCollector;
@ -41,7 +40,6 @@ public class Java8RuntimeMetricsInstaller implements AgentListener {
OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
List<AutoCloseable> observables = new ArrayList<>();
observables.addAll(BufferPools.registerObservers(openTelemetry));
observables.addAll(Classes.registerObservers(openTelemetry));
observables.addAll(Cpu.registerObservers(openTelemetry));
observables.addAll(GarbageCollector.registerObservers(openTelemetry));

View File

@ -41,64 +41,45 @@ Threads.registerObservers(openTelemetry);
GarbageCollector.registerObservers(openTelemetry);
```
## Stable JVM metrics preview
If you want to enable the preview of the stable JVM semantic conventions, you need to set either
the `otel.semconv-stability.opt-in` system property or the `OTEL_SEMCONV_STABILITY_OPT_IN`
environment variable to one of the following values:
- `jvm` - this will make the runtime metrics emit only the new, stable conventions, and stop
emitting the old experimental conventions that the instrumentation emitted previously.
- `jvm/dup` - emit both the old and the stable JVM conventions, allowing for a seamless transition.
Note that the `otel.semconv-stability.opt-in` setting is a comma-separated list, and you can specify
more than one value, e.g. `-Dotel.semconv-stability.opt-in=http,jvm`.
## Garbage Collector Dependent Metrics
The attributes reported on the memory metrics (`process.runtime.jvm.memory.*`) and gc metrics (`process.runtime.jvm.gc.*`) are dependent on the garbage collector used by the application, since each garbage collector organizes memory pools differently and has different strategies for reclaiming memory during garbage collection.
The attributes reported on the memory metrics (`jvm.memory.*`) and gc metrics (`jvm.gc.*`) are dependent on the garbage collector used by the application, since each garbage collector organizes memory pools differently and has different strategies for reclaiming memory during garbage collection.
The following lists attributes reported for a variety of garbage collectors. Notice that attributes are not necessarily constant across `*.init`, `*.usage`, `*.committed`, and `*.limit` since not all memory pools report a limit.
The following lists attributes reported for a variety of garbage collectors. Notice that attributes are not necessarily constant across `*.used`, `*.committed`, and `*.limit` since not all memory pools report a limit.
- CMS Garbage Collector
- `process.runtime.jvm.memory.init`: {pool=Compressed Class Space,type=non_heap}, {pool=Par Eden Space,type=heap}, {pool=Tenured Gen,type=heap}, {pool=Par Survivor Space,type=heap}, {pool=Code Cache,type=non_heap}, {pool=Metaspace,type=non_heap}
- `process.runtime.jvm.memory.usage`: {pool=Compressed Class Space,type=non_heap}, {pool=Par Eden Space,type=heap}, {pool=Tenured Gen,type=heap}, {pool=Par Survivor Space,type=heap}, {pool=Code Cache,type=non_heap}, {pool=Metaspace,type=non_heap}
- `process.runtime.jvm.memory.committed`: {pool=Compressed Class Space,type=non_heap}, {pool=Par Eden Space,type=heap}, {pool=Tenured Gen,type=heap}, {pool=Par Survivor Space,type=heap}, {pool=Code Cache,type=non_heap}, {pool=Metaspace,type=non_heap}
- `process.runtime.jvm.memory.limit`: {pool=Compressed Class Space,type=non_heap}, {pool=Par Eden Space,type=heap}, {pool=Tenured Gen,type=heap}, {pool=Par Survivor Space,type=heap}, {pool=Code Cache,type=non_heap}
- `process.runtime.jvm.memory.usage_after_last_gc`: {pool=Par Eden Space,type=heap}, {pool=Tenured Gen,type=heap}, {pool=Par Survivor Space,type=heap}
- `process.runtime.jvm.gc.duration`: {action=end of minor GC,gc=ParNew}, {action=end of major GC,gc=MarkSweepCompact}
- `jvm.memory.used`: {jvm.memory.pool.name=Compressed Class Space,jvm.memory.type=non_heap}, {jvm.memory.pool.name=Par Eden Space,jvm.memory.type=heap}, {jvm.memory.pool.name=Tenured Gen,jvm.memory.type=heap}, {jvm.memory.pool.name=Par Survivor Space,jvm.memory.type=heap}, {jvm.memory.pool.name=Code Cache,jvm.memory.type=non_heap}, {jvm.memory.pool.name=Metaspace,jvm.memory.type=non_heap}
- `jvm.memory.committed`: {jvm.memory.pool.name=Compressed Class Space,jvm.memory.type=non_heap}, {jvm.memory.pool.name=Par Eden Space,jvm.memory.type=heap}, {jvm.memory.pool.name=Tenured Gen,jvm.memory.type=heap}, {jvm.memory.pool.name=Par Survivor Space,jvm.memory.type=heap}, {jvm.memory.pool.name=Code Cache,jvm.memory.type=non_heap}, {jvm.memory.pool.name=Metaspace,jvm.memory.type=non_heap}
- `jvm.memory.limit`: {jvm.memory.pool.name=Compressed Class Space,jvm.memory.type=non_heap}, {jvm.memory.pool.name=Par Eden Space,jvm.memory.type=heap}, {jvm.memory.pool.name=Tenured Gen,jvm.memory.type=heap}, {jvm.memory.pool.name=Par Survivor Space,jvm.memory.type=heap}, {jvm.memory.pool.name=Code Cache,jvm.memory.type=non_heap}
- `jvm.memory.used_after_last_gc`: {jvm.memory.pool.name=Par Eden Space,jvm.memory.type=heap}, {jvm.memory.pool.name=Tenured Gen,jvm.memory.type=heap}, {jvm.memory.pool.name=Par Survivor Space,jvm.memory.type=heap}
- `jvm.gc.duration`: {jvm.gc.action=end of minor GC,jvm.gc.name=ParNew}, {jvm.gc.action=end of major GC,jvm.gc.name=MarkSweepCompact}
- G1 Garbage Collector
- `process.runtime.jvm.memory.init`: {pool=G1 Survivor Space,type=heap}, {pool=G1 Eden Space,type=heap}, {pool=CodeCache,type=non_heap}, {pool=G1 Old Gen,type=heap}, {pool=Compressed Class Space,type=non_heap}, {pool=Metaspace,type=non_heap}
- `process.runtime.jvm.memory.usage`: {pool=G1 Survivor Space,type=heap}, {pool=G1 Eden Space,type=heap}, {pool=CodeCache,type=non_heap}, {pool=G1 Old Gen,type=heap}, {pool=Compressed Class Space,type=non_heap}, {pool=Metaspace,type=non_heap}
- `process.runtime.jvm.memory.committed`: {pool=G1 Survivor Space,type=heap}, {pool=G1 Eden Space,type=heap}, {pool=CodeCache,type=non_heap}, {pool=G1 Old Gen,type=heap}, {pool=Compressed Class Space,type=non_heap}, {pool=Metaspace,type=non_heap}
- `process.runtime.jvm.memory.limit`: {pool=CodeCache,type=non_heap}, {pool=G1 Old Gen,type=heap}, {pool=Compressed Class Space,type=non_heap}
- `process.runtime.jvm.memory.usage_after_last_gc`: {pool=G1 Survivor Space,type=heap}, {pool=G1 Eden Space,type=heap}, {pool=G1 Old Gen,type=heap}
- `process.runtime.jvm.gc.duration`: {action=end of minor GC,gc=G1 Young Generation}, {action=end of major GC,gc=G1 Old Generation}
- `jvm.memory.used`: {jvm.memory.pool.name=G1 Survivor Space,jvm.memory.type=heap}, {jvm.memory.pool.name=G1 Eden Space,jvm.memory.type=heap}, {jvm.memory.pool.name=CodeCache,jvm.memory.type=non_heap}, {jvm.memory.pool.name=G1 Old Gen,jvm.memory.type=heap}, {jvm.memory.pool.name=Compressed Class Space,jvm.memory.type=non_heap}, {jvm.memory.pool.name=Metaspace,jvm.memory.type=non_heap}
- `jvm.memory.committed`: {jvm.memory.pool.name=G1 Survivor Space,jvm.memory.type=heap}, {jvm.memory.pool.name=G1 Eden Space,jvm.memory.type=heap}, {jvm.memory.pool.name=CodeCache,jvm.memory.type=non_heap}, {jvm.memory.pool.name=G1 Old Gen,jvm.memory.type=heap}, {jvm.memory.pool.name=Compressed Class Space,jvm.memory.type=non_heap}, {jvm.memory.pool.name=Metaspace,jvm.memory.type=non_heap}
- `jvm.memory.limit`: {jvm.memory.pool.name=CodeCache,jvm.memory.type=non_heap}, {jvm.memory.pool.name=G1 Old Gen,jvm.memory.type=heap}, {jvm.memory.pool.name=Compressed Class Space,jvm.memory.type=non_heap}
- `jvm.memory.used_after_last_gc`: {jvm.memory.pool.name=G1 Survivor Space,jvm.memory.type=heap}, {jvm.memory.pool.name=G1 Eden Space,jvm.memory.type=heap}, {jvm.memory.pool.name=G1 Old Gen,jvm.memory.type=heap}
- `jvm.gc.duration`: {jvm.gc.action=end of minor GC,jvm.gc.name=G1 Young Generation}, {jvm.gc.action=end of major GC,jvm.gc.name=G1 Old Generation}
- Parallel Garbage Collector
- `process.runtime.jvm.memory.init`: {pool=CodeCache,type=non_heap}, {pool=PS Survivor Space,type=heap}, {pool=PS Old Gen,type=heap}, {pool=PS Eden Space,type=heap}, {pool=Compressed Class Space,type=non_heap}, {pool=Metaspace,type=non_heap}
- `process.runtime.jvm.memory.usage`: {pool=CodeCache,type=non_heap}, {pool=PS Survivor Space,type=heap}, {pool=PS Old Gen,type=heap}, {pool=PS Eden Space,type=heap}, {pool=Compressed Class Space,type=non_heap}, {pool=Metaspace,type=non_heap}
- `process.runtime.jvm.memory.committed`: {pool=CodeCache,type=non_heap}, {pool=PS Survivor Space,type=heap}, {pool=PS Old Gen,type=heap}, {pool=PS Eden Space,type=heap}, {pool=Compressed Class Space,type=non_heap}, {pool=Metaspace,type=non_heap}
- `process.runtime.jvm.memory.limit`: {pool=CodeCache,type=non_heap}, {pool=PS Survivor Space,type=heap}, {pool=PS Old Gen,type=heap}, {pool=PS Eden Space,type=heap}, {pool=Compressed Class Space,type=non_heap}
- `process.runtime.jvm.memory.usage_after_last_gc`: {pool=PS Survivor Space,type=heap}, {pool=PS Old Gen,type=heap}, {pool=PS Eden Space,type=heap}
- `process.runtime.jvm.gc.duration`: {action=end of major GC,gc=PS MarkSweep}, {action=end of minor GC,gc=PS Scavenge}
- `jvm.memory.used`: {jvm.memory.pool.name=CodeCache,jvm.memory.type=non_heap}, {jvm.memory.pool.name=PS Survivor Space,jvm.memory.type=heap}, {jvm.memory.pool.name=PS Old Gen,jvm.memory.type=heap}, {jvm.memory.pool.name=PS Eden Space,jvm.memory.type=heap}, {jvm.memory.pool.name=Compressed Class Space,jvm.memory.type=non_heap}, {jvm.memory.pool.name=Metaspace,jvm.memory.type=non_heap}
- `jvm.memory.committed`: {jvm.memory.pool.name=CodeCache,jvm.memory.type=non_heap}, {jvm.memory.pool.name=PS Survivor Space,jvm.memory.type=heap}, {jvm.memory.pool.name=PS Old Gen,jvm.memory.type=heap}, {jvm.memory.pool.name=PS Eden Space,jvm.memory.type=heap}, {jvm.memory.pool.name=Compressed Class Space,jvm.memory.type=non_heap}, {jvm.memory.pool.name=Metaspace,jvm.memory.type=non_heap}
- `jvm.memory.limit`: {jvm.memory.pool.name=CodeCache,jvm.memory.type=non_heap}, {jvm.memory.pool.name=PS Survivor Space,jvm.memory.type=heap}, {jvm.memory.pool.name=PS Old Gen,jvm.memory.type=heap}, {jvm.memory.pool.name=PS Eden Space,jvm.memory.type=heap}, {jvm.memory.pool.name=Compressed Class Space,jvm.memory.type=non_heap}
- `jvm.memory.used_after_last_gc`: {jvm.memory.pool.name=PS Survivor Space,jvm.memory.type=heap}, {jvm.memory.pool.name=PS Old Gen,jvm.memory.type=heap}, {jvm.memory.pool.name=PS Eden Space,jvm.memory.type=heap}
- `jvm.gc.duration`: {jvm.gc.action=end of major GC,jvm.gc.name=PS MarkSweep}, {jvm.gc.action=end of minor GC,jvm.gc.name=PS Scavenge}
- Serial Garbage Collector
- `process.runtime.jvm.memory.init`: {pool=CodeCache,type=non_heap}, {pool=Tenured Gen,type=heap}, {pool=Eden Space,type=heap}, {pool=Survivor Space,type=heap}, {pool=Compressed Class Space,type=non_heap}, {pool=Metaspace,type=non_heap}
- `process.runtime.jvm.memory.usage`: {pool=CodeCache,type=non_heap}, {pool=Tenured Gen,type=heap}, {pool=Eden Space,type=heap}, {pool=Survivor Space,type=heap}, {pool=Compressed Class Space,type=non_heap}, {pool=Metaspace,type=non_heap}
- `process.runtime.jvm.memory.committed`: {pool=CodeCache,type=non_heap}, {pool=Tenured Gen,type=heap}, {pool=Eden Space,type=heap}, {pool=Survivor Space,type=heap}, {pool=Compressed Class Space,type=non_heap}, {pool=Metaspace,type=non_heap}
- `process.runtime.jvm.memory.limit`: {pool=CodeCache,type=non_heap}, {pool=Tenured Gen,type=heap}, {pool=Eden Space,type=heap}, {pool=Survivor Space,type=heap}, {pool=Compressed Class Space,type=non_heap}
- `process.runtime.jvm.memory.usage_after_last_gc`: {pool=Tenured Gen,type=heap}, {pool=Eden Space,type=heap}, {pool=Survivor Space,type=heap}
- `process.runtime.jvm.gc.duration`: {action=end of minor GC,gc=Copy}, {action=end of major GC,gc=MarkSweepCompact}
- `jvm.memory.used`: {jvm.memory.pool.name=CodeCache,jvm.memory.type=non_heap}, {jvm.memory.pool.name=Tenured Gen,jvm.memory.type=heap}, {jvm.memory.pool.name=Eden Space,jvm.memory.type=heap}, {jvm.memory.pool.name=Survivor Space,jvm.memory.type=heap}, {jvm.memory.pool.name=Compressed Class Space,jvm.memory.type=non_heap}, {jvm.memory.pool.name=Metaspace,jvm.memory.type=non_heap}
- `jvm.memory.committed`: {jvm.memory.pool.name=CodeCache,jvm.memory.type=non_heap}, {jvm.memory.pool.name=Tenured Gen,jvm.memory.type=heap}, {jvm.memory.pool.name=Eden Space,jvm.memory.type=heap}, {jvm.memory.pool.name=Survivor Space,jvm.memory.type=heap}, {jvm.memory.pool.name=Compressed Class Space,jvm.memory.type=non_heap}, {jvm.memory.pool.name=Metaspace,jvm.memory.type=non_heap}
- `jvm.memory.limit`: {jvm.memory.pool.name=CodeCache,jvm.memory.type=non_heap}, {jvm.memory.pool.name=Tenured Gen,jvm.memory.type=heap}, {jvm.memory.pool.name=Eden Space,jvm.memory.type=heap}, {jvm.memory.pool.name=Survivor Space,jvm.memory.type=heap}, {jvm.memory.pool.name=Compressed Class Space,jvm.memory.type=non_heap}
- `jvm.memory.used_after_last_gc`: {jvm.memory.pool.name=Tenured Gen,jvm.memory.type=heap}, {jvm.memory.pool.name=Eden Space,jvm.memory.type=heap}, {jvm.memory.pool.name=Survivor Space,jvm.memory.type=heap}
- `jvm.gc.duration`: {jvm.gc.action=end of minor GC,jvm.gc.name=Copy}, {jvm.gc.action=end of major GC,jvm.gc.name=MarkSweepCompact}
- Shenandoah Garbage Collector
- `process.runtime.jvm.memory.init`: {pool=Metaspace,type=non_heap}, {pool=CodeCache,type=non_heap}, {pool=Shenandoah,type=heap}, {pool=Compressed Class Space,type=non_heap}
- `process.runtime.jvm.memory.usage`: {pool=Metaspace,type=non_heap}, {pool=CodeCache,type=non_heap}, {pool=Shenandoah,type=heap}, {pool=Compressed Class Space,type=non_heap}
- `process.runtime.jvm.memory.committed`: {pool=Metaspace,type=non_heap}, {pool=CodeCache,type=non_heap}, {pool=Shenandoah,type=heap}, {pool=Compressed Class Space,type=non_heap}
- `process.runtime.jvm.memory.limit`: {pool=CodeCache,type=non_heap}, {pool=Shenandoah,type=heap}, {pool=Compressed Class Space,type=non_heap}
- `process.runtime.jvm.memory.usage_after_last_gc`: {pool=Shenandoah,type=heap}
- `process.runtime.jvm.gc.duration`: {action=end of GC cycle,gc=Shenandoah Cycles}, {action=end of GC pause,gc=Shenandoah Pauses}
- `jvm.memory.used`: {jvm.memory.pool.name=Metaspace,jvm.memory.type=non_heap}, {jvm.memory.pool.name=CodeCache,jvm.memory.type=non_heap}, {jvm.memory.pool.name=Shenandoah,jvm.memory.type=heap}, {jvm.memory.pool.name=Compressed Class Space,jvm.memory.type=non_heap}
- `jvm.memory.committed`: {jvm.memory.pool.name=Metaspace,jvm.memory.type=non_heap}, {jvm.memory.pool.name=CodeCache,jvm.memory.type=non_heap}, {jvm.memory.pool.name=Shenandoah,jvm.memory.type=heap}, {jvm.memory.pool.name=Compressed Class Space,jvm.memory.type=non_heap}
- `jvm.memory.limit`: {jvm.memory.pool.name=CodeCache,jvm.memory.type=non_heap}, {jvm.memory.pool.name=Shenandoah,jvm.memory.type=heap}, {jvm.memory.pool.name=Compressed Class Space,jvm.memory.type=non_heap}
- `jvm.memory.used_after_last_gc`: {jvm.memory.pool.name=Shenandoah,jvm.memory.type=heap}
- `jvm.gc.duration`: {jvm.gc.action=end of GC cycle,jvm.gc.name=Shenandoah Cycles}, {jvm.gc.action=end of GC pause,jvm.gc.name=Shenandoah Pauses}
- Z Garbage Collector
- `process.runtime.jvm.memory.init`: {pool=Metaspace,type=non_heap}, {pool=CodeCache,type=non_heap}, {pool=ZHeap,type=heap}, {pool=Compressed Class Space,type=non_heap}
- `process.runtime.jvm.memory.usage`: {pool=Metaspace,type=non_heap}, {pool=CodeCache,type=non_heap}, {pool=ZHeap,type=heap}, {pool=Compressed Class Space,type=non_heap}
- `process.runtime.jvm.memory.committed`: {pool=Metaspace,type=non_heap}, {pool=CodeCache,type=non_heap}, {pool=ZHeap,type=heap}, {pool=Compressed Class Space,type=non_heap}
- `process.runtime.jvm.memory.limit`: {pool=CodeCache,type=non_heap}, {pool=ZHeap,type=heap}, {pool=Compressed Class Space,type=non_heap}
- `process.runtime.jvm.memory.usage_after_last_gc`: {pool=ZHeap,type=heap}
- `process.runtime.jvm.gc.duration`: {action=end of GC cycle,gc=ZGC Cycles}, {action=end of GC pause,gc=ZGC Pauses}
- `jvm.memory.used`: {jvm.memory.pool.name=Metaspace,jvm.memory.type=non_heap}, {jvm.memory.pool.name=CodeCache,jvm.memory.type=non_heap}, {jvm.memory.pool.name=ZHeap,jvm.memory.type=heap}, {jvm.memory.pool.name=Compressed Class Space,jvm.memory.type=non_heap}
- `jvm.memory.committed`: {jvm.memory.pool.name=Metaspace,jvm.memory.type=non_heap}, {jvm.memory.pool.name=CodeCache,jvm.memory.type=non_heap}, {jvm.memory.pool.name=ZHeap,jvm.memory.type=heap}, {jvm.memory.pool.name=Compressed Class Space,jvm.memory.type=non_heap}
- `jvm.memory.limit`: {jvm.memory.pool.name=CodeCache,jvm.memory.type=non_heap}, {jvm.memory.pool.name=ZHeap,jvm.memory.type=heap}, {jvm.memory.pool.name=Compressed Class Space,jvm.memory.type=non_heap}
- `jvm.memory.used_after_last_gc`: {jvm.memory.pool.name=ZHeap,jvm.memory.type=heap}
- `jvm.gc.duration`: {jvm.gc.action=end of GC cycle,jvm.gc.name=ZGC Cycles}, {jvm.gc.action=end of GC pause,jvm.gc.name=ZGC Pauses}

View File

@ -8,27 +8,3 @@ dependencies {
testImplementation(project(":testing-common"))
}
testing {
suites {
val testStableSemconv by registering(JvmTestSuite::class) {
dependencies {
implementation(project())
implementation(project(":testing-common"))
}
targets {
all {
testTask.configure {
jvmArgs("-Dotel.semconv-stability.opt-in=jvm")
}
}
}
}
}
}
tasks {
check {
dependsOn(testing.suites)
}
}

View File

@ -1,101 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.runtimemetrics.java8;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.ObservableLongMeasurement;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsUtil;
import java.lang.management.BufferPoolMXBean;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* Registers measurements that generate metrics about buffer pools.
*
* <p>Example usage:
*
* <pre>{@code
* BufferPools.registerObservers(GlobalOpenTelemetry.get());
* }</pre>
*
* <p>Example metrics being exported:
*
* <pre>
* process.runtime.jvm.buffer.usage.usage{pool="buffer_pool"} 500
* process.runtime.jvm.buffer.usage.max{pool="buffer_pool"} 1500
* process.runtime.jvm.buffer.usage.count{pool="buffer_pool"} 15
* </pre>
*/
public final class BufferPools {
private static final AttributeKey<String> POOL_KEY = AttributeKey.stringKey("pool");
/** Register observers for java runtime buffer pool metrics. */
public static List<AutoCloseable> registerObservers(OpenTelemetry openTelemetry) {
List<BufferPoolMXBean> bufferBeans =
ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class);
return registerObservers(openTelemetry, bufferBeans);
}
// Visible for testing
static List<AutoCloseable> registerObservers(
OpenTelemetry openTelemetry, List<BufferPoolMXBean> bufferBeans) {
// buffer pool metrics are experimental in the new semconv
if (!SemconvStability.emitOldJvmSemconv()) {
return Collections.emptyList();
}
List<AutoCloseable> observables = new ArrayList<>();
Meter meter = JmxRuntimeMetricsUtil.getMeter(openTelemetry);
observables.add(
meter
.upDownCounterBuilder("process.runtime.jvm.buffer.usage")
.setDescription("Memory that the Java virtual machine is using for this buffer pool")
.setUnit("By")
.buildWithCallback(callback(bufferBeans, BufferPoolMXBean::getMemoryUsed)));
observables.add(
meter
.upDownCounterBuilder("process.runtime.jvm.buffer.limit")
.setDescription("Total capacity of the buffers in this pool")
.setUnit("By")
.buildWithCallback(callback(bufferBeans, BufferPoolMXBean::getTotalCapacity)));
observables.add(
meter
.upDownCounterBuilder("process.runtime.jvm.buffer.count")
.setDescription("The number of buffers in the pool")
.setUnit("{buffers}")
.buildWithCallback(callback(bufferBeans, BufferPoolMXBean::getCount)));
return observables;
}
// Visible for testing
static Consumer<ObservableLongMeasurement> callback(
List<BufferPoolMXBean> bufferPools, Function<BufferPoolMXBean, Long> extractor) {
List<Attributes> attributeSets = new ArrayList<>(bufferPools.size());
for (BufferPoolMXBean pool : bufferPools) {
attributeSets.add(Attributes.builder().put(POOL_KEY, pool.getName()).build());
}
return measurement -> {
for (int i = 0; i < bufferPools.size(); i++) {
Attributes attributes = attributeSets.get(i);
long value = extractor.apply(bufferPools.get(i));
if (value != -1) {
measurement.record(value, attributes);
}
}
};
}
private BufferPools() {}
}

View File

@ -7,7 +7,6 @@ package io.opentelemetry.instrumentation.runtimemetrics.java8;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsUtil;
import java.lang.management.ClassLoadingMXBean;
import java.lang.management.ManagementFactory;
@ -15,7 +14,10 @@ import java.util.ArrayList;
import java.util.List;
/**
* Registers measurements that generate metrics about JVM classes.
* Registers measurements that generate metrics about JVM classes. The metrics generated by this
* class follow <a
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/docs/runtime/jvm-metrics.md">the
* stable JVM metrics semantic conventions</a>.
*
* <p>Example usage:
*
@ -26,19 +28,6 @@ import java.util.List;
* <p>Example metrics being exported:
*
* <pre>
* process.runtime.jvm.classes.loaded 100
* process.runtime.jvm.classes.unloaded 2
* process.runtime.jvm.classes.current_loaded 98
* </pre>
*
* <p>In case you enable the preview of stable JVM semantic conventions (e.g. by setting the {@code
* otel.semconv-stability.opt-in} system property to {@code jvm}), the metrics being exported will
* follow <a
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/docs/runtime/jvm-metrics.md">the
* most recent JVM semantic conventions</a>. This is how the example above looks when stable JVM
* semconv is enabled:
*
* <pre>
* jvm.class.loaded 100
* jvm.class.unloaded 2
* jvm.class.count 98
@ -59,59 +48,30 @@ public final class Classes {
Meter meter = JmxRuntimeMetricsUtil.getMeter(openTelemetry);
List<AutoCloseable> observables = new ArrayList<>();
if (SemconvStability.emitOldJvmSemconv()) {
observables.add(
meter
.counterBuilder("process.runtime.jvm.classes.loaded")
.setDescription("Number of classes loaded since JVM start")
.setUnit("{class}")
.buildWithCallback(
observableMeasurement ->
observableMeasurement.record(classBean.getTotalLoadedClassCount())));
observables.add(
meter
.counterBuilder("process.runtime.jvm.classes.unloaded")
.setDescription("Number of classes unloaded since JVM start")
.setUnit("{class}")
.buildWithCallback(
observableMeasurement ->
observableMeasurement.record(classBean.getUnloadedClassCount())));
observables.add(
meter
.upDownCounterBuilder("process.runtime.jvm.classes.current_loaded")
.setDescription("Number of classes currently loaded")
.setUnit("{class}")
.buildWithCallback(
observableMeasurement ->
observableMeasurement.record(classBean.getLoadedClassCount())));
}
if (SemconvStability.emitStableJvmSemconv()) {
observables.add(
meter
.counterBuilder("jvm.class.loaded")
.setDescription("Number of classes loaded since JVM start.")
.setUnit("{class}")
.buildWithCallback(
observableMeasurement ->
observableMeasurement.record(classBean.getTotalLoadedClassCount())));
observables.add(
meter
.counterBuilder("jvm.class.unloaded")
.setDescription("Number of classes unloaded since JVM start.")
.setUnit("{class}")
.buildWithCallback(
observableMeasurement ->
observableMeasurement.record(classBean.getUnloadedClassCount())));
observables.add(
meter
.upDownCounterBuilder("jvm.class.count")
.setDescription("Number of classes currently loaded.")
.setUnit("{class}")
.buildWithCallback(
observableMeasurement ->
observableMeasurement.record(classBean.getLoadedClassCount())));
}
observables.add(
meter
.counterBuilder("jvm.class.loaded")
.setDescription("Number of classes loaded since JVM start.")
.setUnit("{class}")
.buildWithCallback(
observableMeasurement ->
observableMeasurement.record(classBean.getTotalLoadedClassCount())));
observables.add(
meter
.counterBuilder("jvm.class.unloaded")
.setDescription("Number of classes unloaded since JVM start.")
.setUnit("{class}")
.buildWithCallback(
observableMeasurement ->
observableMeasurement.record(classBean.getUnloadedClassCount())));
observables.add(
meter
.upDownCounterBuilder("jvm.class.count")
.setDescription("Number of classes currently loaded.")
.setUnit("{class}")
.buildWithCallback(
observableMeasurement ->
observableMeasurement.record(classBean.getLoadedClassCount())));
return observables;
}

View File

@ -7,11 +7,8 @@ package io.opentelemetry.instrumentation.runtimemetrics.java8;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.CpuMethods;
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsUtil;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@ -20,7 +17,10 @@ import java.util.function.Supplier;
import javax.annotation.Nullable;
/**
* Registers measurements that generate metrics about CPU.
* Registers measurements that generate metrics about CPU. The metrics generated by this class
* follow <a
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/docs/runtime/jvm-metrics.md">the
* stable JVM metrics semantic conventions</a>.
*
* <p>Example usage:
*
@ -31,19 +31,6 @@ import javax.annotation.Nullable;
* <p>Example metrics being exported:
*
* <pre>
* process.runtime.jvm.system.cpu.load_1m 2.2
* process.runtime.jvm.system.cpu.utilization 0.15
* process.runtime.jvm.cpu.utilization 0.1
* </pre>
*
* <p>In case you enable the preview of stable JVM semantic conventions (e.g. by setting the {@code
* otel.semconv-stability.opt-in} system property to {@code jvm}), the metrics being exported will
* follow <a
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/docs/runtime/jvm-metrics.md">the
* most recent JVM semantic conventions</a>. This is how the example above looks when stable JVM
* semconv is enabled:
*
* <pre>
* jvm.cpu.time 20.42
* jvm.cpu.count 8
* jvm.cpu.recent_utilization 0.1
@ -60,105 +47,58 @@ public final class Cpu {
public static List<AutoCloseable> registerObservers(OpenTelemetry openTelemetry) {
return INSTANCE.registerObservers(
openTelemetry,
ManagementFactory.getOperatingSystemMXBean(),
Runtime.getRuntime()::availableProcessors,
CpuMethods.processCpuTime(),
CpuMethods.systemCpuUtilization(),
CpuMethods.processCpuUtilization());
}
// Visible for testing
List<AutoCloseable> registerObservers(
OpenTelemetry openTelemetry,
OperatingSystemMXBean osBean,
IntSupplier availableProcessors,
@Nullable Supplier<Long> processCpuTime,
@Nullable Supplier<Double> systemCpuUtilization,
@Nullable Supplier<Double> processCpuUtilization) {
Meter meter = JmxRuntimeMetricsUtil.getMeter(openTelemetry);
List<AutoCloseable> observables = new ArrayList<>();
if (SemconvStability.emitOldJvmSemconv()) {
if (processCpuTime != null) {
observables.add(
meter
.gaugeBuilder("process.runtime.jvm.system.cpu.load_1m")
.setDescription("Average CPU load of the whole system for the last minute")
.setUnit("{run_queue_item}")
.counterBuilder("jvm.cpu.time")
.ofDoubles()
.setDescription("CPU time used by the process as reported by the JVM.")
.setUnit("s")
.buildWithCallback(
observableMeasurement -> {
double loadAverage = osBean.getSystemLoadAverage();
if (loadAverage >= 0) {
observableMeasurement.record(loadAverage);
Long cpuTimeNanos = processCpuTime.get();
if (cpuTimeNanos != null && cpuTimeNanos >= 0) {
observableMeasurement.record(cpuTimeNanos / NANOS_PER_S);
}
}));
if (systemCpuUtilization != null) {
observables.add(
meter
.gaugeBuilder("process.runtime.jvm.system.cpu.utilization")
.setDescription("Recent cpu utilization for the whole system")
.setUnit("1")
.buildWithCallback(
observableMeasurement -> {
Double cpuUsage = systemCpuUtilization.get();
if (cpuUsage != null && cpuUsage >= 0) {
observableMeasurement.record(cpuUsage);
}
}));
}
if (processCpuUtilization != null) {
observables.add(
meter
.gaugeBuilder("process.runtime.jvm.cpu.utilization")
.setDescription("Recent cpu utilization for the process")
.setUnit("1")
.buildWithCallback(
observableMeasurement -> {
Double cpuUsage = processCpuUtilization.get();
if (cpuUsage != null && cpuUsage >= 0) {
observableMeasurement.record(cpuUsage);
}
}));
}
}
if (SemconvStability.emitStableJvmSemconv()) {
if (processCpuTime != null) {
observables.add(
meter
.counterBuilder("jvm.cpu.time")
.ofDoubles()
.setDescription("CPU time used by the process as reported by the JVM.")
.setUnit("s")
.buildWithCallback(
observableMeasurement -> {
Long cpuTimeNanos = processCpuTime.get();
if (cpuTimeNanos != null && cpuTimeNanos >= 0) {
observableMeasurement.record(cpuTimeNanos / NANOS_PER_S);
}
}));
}
observables.add(
meter
.upDownCounterBuilder("jvm.cpu.count")
.setDescription("Number of processors available to the Java virtual machine.")
.setUnit("{cpu}")
.buildWithCallback(
observableMeasurement ->
observableMeasurement.record(availableProcessors.getAsInt())));
if (processCpuUtilization != null) {
observables.add(
meter
.upDownCounterBuilder("jvm.cpu.count")
.setDescription("Number of processors available to the Java virtual machine.")
.setUnit("{cpu}")
.gaugeBuilder("jvm.cpu.recent_utilization")
.setDescription("Recent CPU utilization for the process as reported by the JVM.")
.setUnit("1")
.buildWithCallback(
observableMeasurement ->
observableMeasurement.record(availableProcessors.getAsInt())));
if (processCpuUtilization != null) {
observables.add(
meter
.gaugeBuilder("jvm.cpu.recent_utilization")
.setDescription("Recent CPU utilization for the process as reported by the JVM.")
.setUnit("1")
.buildWithCallback(
observableMeasurement -> {
Double cpuUsage = processCpuUtilization.get();
if (cpuUsage != null && cpuUsage >= 0) {
observableMeasurement.record(cpuUsage);
}
}));
}
observableMeasurement -> {
Double cpuUsage = processCpuUtilization.get();
if (cpuUsage != null && cpuUsage >= 0) {
observableMeasurement.record(cpuUsage);
}
}));
}
return observables;
}

View File

@ -7,7 +7,6 @@ package io.opentelemetry.instrumentation.runtimemetrics.java8;
import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import com.sun.management.GarbageCollectionNotificationInfo;
@ -16,7 +15,6 @@ import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsUtil;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
@ -26,7 +24,6 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.management.Notification;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
@ -34,7 +31,10 @@ import javax.management.NotificationListener;
import javax.management.openmbean.CompositeData;
/**
* Registers instruments that generate metrics about JVM garbage collection.
* Registers instruments that generate metrics about JVM garbage collection. The metrics generated
* by this class follow <a
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/docs/runtime/jvm-metrics.md">the
* stable JVM metrics semantic conventions</a>.
*
* <p>Example usage:
*
@ -45,17 +45,6 @@ import javax.management.openmbean.CompositeData;
* <p>Example metrics being exported:
*
* <pre>
* process.runtime.jvm.gc.duration{gc="G1 Young Generation",action="end of minor GC"} 0.022
* </pre>
*
* <p>In case you enable the preview of stable JVM semantic conventions (e.g. by setting the {@code
* otel.semconv-stability.opt-in} system property to {@code jvm}), the metrics being exported will
* follow <a
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/docs/runtime/jvm-metrics.md">the
* most recent JVM semantic conventions</a>. This is how the example above looks when stable JVM
* semconv is enabled:
*
* <pre>
* jvm.gc.duration{jvm.gc.name="G1 Young Generation",jvm.gc.action="end of minor GC"} 0.022
* </pre>
*/
@ -65,9 +54,6 @@ public final class GarbageCollector {
private static final double MILLIS_PER_S = TimeUnit.SECONDS.toMillis(1);
private static final AttributeKey<String> GC_KEY = stringKey("gc");
private static final AttributeKey<String> ACTION_KEY = stringKey("action");
// TODO: use the opentelemetry-semconv classes once we have metrics attributes there
private static final AttributeKey<String> JVM_GC_NAME = stringKey("jvm.gc.name");
private static final AttributeKey<String> JVM_GC_ACTION = stringKey("jvm.gc.action");
@ -101,27 +87,13 @@ public final class GarbageCollector {
Function<Notification, GarbageCollectionNotificationInfo> notificationInfoExtractor) {
Meter meter = JmxRuntimeMetricsUtil.getMeter(openTelemetry);
DoubleHistogram oldGcDuration = null;
DoubleHistogram stableGcDuration = null;
if (SemconvStability.emitOldJvmSemconv()) {
oldGcDuration =
meter
.histogramBuilder("process.runtime.jvm.gc.duration")
.setDescription("Duration of JVM garbage collection actions")
.setUnit("s")
.setExplicitBucketBoundariesAdvice(emptyList())
.build();
}
if (SemconvStability.emitStableJvmSemconv()) {
stableGcDuration =
meter
.histogramBuilder("jvm.gc.duration")
.setDescription("Duration of JVM garbage collection actions.")
.setUnit("s")
.setExplicitBucketBoundariesAdvice(GC_DURATION_BUCKETS)
.build();
}
DoubleHistogram gcDuration =
meter
.histogramBuilder("jvm.gc.duration")
.setDescription("Duration of JVM garbage collection actions.")
.setUnit("s")
.setExplicitBucketBoundariesAdvice(GC_DURATION_BUCKETS)
.build();
List<AutoCloseable> result = new ArrayList<>();
for (GarbageCollectorMXBean gcBean : gcBeans) {
@ -130,7 +102,7 @@ public final class GarbageCollector {
}
NotificationEmitter notificationEmitter = (NotificationEmitter) gcBean;
GcNotificationListener listener =
new GcNotificationListener(oldGcDuration, stableGcDuration, notificationInfoExtractor);
new GcNotificationListener(gcDuration, notificationInfoExtractor);
notificationEmitter.addNotificationListener(listener, GC_FILTER, null);
result.add(() -> notificationEmitter.removeNotificationListener(listener));
}
@ -139,17 +111,14 @@ public final class GarbageCollector {
private static final class GcNotificationListener implements NotificationListener {
@Nullable private final DoubleHistogram oldGcDuration;
@Nullable private final DoubleHistogram stableGcDuration;
private final DoubleHistogram gcDuration;
private final Function<Notification, GarbageCollectionNotificationInfo>
notificationInfoExtractor;
private GcNotificationListener(
@Nullable DoubleHistogram oldGcDuration,
@Nullable DoubleHistogram stableGcDuration,
DoubleHistogram gcDuration,
Function<Notification, GarbageCollectionNotificationInfo> notificationInfoExtractor) {
this.oldGcDuration = oldGcDuration;
this.stableGcDuration = stableGcDuration;
this.gcDuration = gcDuration;
this.notificationInfoExtractor = notificationInfoExtractor;
}
@ -162,13 +131,7 @@ public final class GarbageCollector {
String gcAction = notificationInfo.getGcAction();
double duration = notificationInfo.getGcInfo().getDuration() / MILLIS_PER_S;
if (oldGcDuration != null) {
oldGcDuration.record(duration, Attributes.of(GC_KEY, gcName, ACTION_KEY, gcAction));
}
if (stableGcDuration != null) {
stableGcDuration.record(
duration, Attributes.of(JVM_GC_NAME, gcName, JVM_GC_ACTION, gcAction));
}
gcDuration.record(duration, Attributes.of(JVM_GC_NAME, gcName, JVM_GC_ACTION, gcAction));
}
}

View File

@ -12,7 +12,6 @@ import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.ObservableLongMeasurement;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsUtil;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
@ -24,7 +23,10 @@ import java.util.function.Consumer;
import java.util.function.Function;
/**
* Registers measurements that generate metrics about JVM memory pools.
* Registers measurements that generate metrics about JVM memory pools. The metrics generated by
* this class follow <a
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/docs/runtime/jvm-metrics.md">the
* stable JVM metrics semantic conventions</a>.
*
* <p>Example usage:
*
@ -32,25 +34,7 @@ import java.util.function.Function;
* MemoryPools.registerObservers(GlobalOpenTelemetry.get());
* }</pre>
*
* <p>Example metrics being exported: Component
*
* <pre>
* 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.limit{type="heap",pool="G1 Eden Space"} 4000000
* process.runtime.jvm.memory.usage_after_last_gc{type="heap",pool="G1 Eden Space"} 1500000
* 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
* </pre>
*
* <p>In case you enable the preview of stable JVM semantic conventions (e.g. by setting the {@code
* otel.semconv-stability.opt-in} system property to {@code jvm}), the metrics being exported will
* follow <a
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/docs/runtime/jvm-metrics.md">the
* most recent JVM semantic conventions</a>. This is how the example above looks when stable JVM
* semconv is enabled:
* <p>Example metrics being exported:
*
* <pre>
* jvm.memory.used{type="heap",pool="G1 Eden Space"} 2500000
@ -63,9 +47,6 @@ import java.util.function.Function;
*/
public final class MemoryPools {
private static final AttributeKey<String> TYPE_KEY = stringKey("type");
private static final AttributeKey<String> POOL_KEY = stringKey("pool");
// TODO: use the opentelemetry-semconv classes once we have metrics attributes there
private static final AttributeKey<String> JVM_MEMORY_POOL_NAME =
stringKey("jvm.memory.pool.name");
@ -85,121 +66,55 @@ public final class MemoryPools {
Meter meter = JmxRuntimeMetricsUtil.getMeter(openTelemetry);
List<AutoCloseable> observables = new ArrayList<>();
if (SemconvStability.emitOldJvmSemconv()) {
observables.add(
meter
.upDownCounterBuilder("process.runtime.jvm.memory.usage")
.setDescription("Measure of memory used")
.setUnit("By")
.buildWithCallback(
callback(
POOL_KEY,
TYPE_KEY,
poolBeans,
MemoryPoolMXBean::getUsage,
MemoryUsage::getUsed)));
observables.add(
meter
.upDownCounterBuilder("process.runtime.jvm.memory.init")
.setDescription("Measure of initial memory requested")
.setUnit("By")
.buildWithCallback(
callback(
POOL_KEY,
TYPE_KEY,
poolBeans,
MemoryPoolMXBean::getUsage,
MemoryUsage::getInit)));
observables.add(
meter
.upDownCounterBuilder("process.runtime.jvm.memory.committed")
.setDescription("Measure of memory committed")
.setUnit("By")
.buildWithCallback(
callback(
POOL_KEY,
TYPE_KEY,
poolBeans,
MemoryPoolMXBean::getUsage,
MemoryUsage::getCommitted)));
observables.add(
meter
.upDownCounterBuilder("process.runtime.jvm.memory.limit")
.setDescription("Measure of max obtainable memory")
.setUnit("By")
.buildWithCallback(
callback(
POOL_KEY,
TYPE_KEY,
poolBeans,
MemoryPoolMXBean::getUsage,
MemoryUsage::getMax)));
observables.add(
meter
.upDownCounterBuilder("process.runtime.jvm.memory.usage_after_last_gc")
.setDescription(
"Measure of memory used after the most recent garbage collection event on this pool")
.setUnit("By")
.buildWithCallback(
callback(
POOL_KEY,
TYPE_KEY,
poolBeans,
MemoryPoolMXBean::getCollectionUsage,
MemoryUsage::getUsed)));
}
if (SemconvStability.emitStableJvmSemconv()) {
observables.add(
meter
.upDownCounterBuilder("jvm.memory.used")
.setDescription("Measure of memory used.")
.setUnit("By")
.buildWithCallback(
callback(
JVM_MEMORY_POOL_NAME,
JVM_MEMORY_TYPE,
poolBeans,
MemoryPoolMXBean::getUsage,
MemoryUsage::getUsed)));
observables.add(
meter
.upDownCounterBuilder("jvm.memory.committed")
.setDescription("Measure of memory committed.")
.setUnit("By")
.buildWithCallback(
callback(
JVM_MEMORY_POOL_NAME,
JVM_MEMORY_TYPE,
poolBeans,
MemoryPoolMXBean::getUsage,
MemoryUsage::getCommitted)));
observables.add(
meter
.upDownCounterBuilder("jvm.memory.limit")
.setDescription("Measure of max obtainable memory.")
.setUnit("By")
.buildWithCallback(
callback(
JVM_MEMORY_POOL_NAME,
JVM_MEMORY_TYPE,
poolBeans,
MemoryPoolMXBean::getUsage,
MemoryUsage::getMax)));
observables.add(
meter
.upDownCounterBuilder("jvm.memory.used_after_last_gc")
.setDescription(
"Measure of memory used, as measured after the most recent garbage collection event on this pool.")
.setUnit("By")
.buildWithCallback(
callback(
JVM_MEMORY_POOL_NAME,
JVM_MEMORY_TYPE,
poolBeans,
MemoryPoolMXBean::getCollectionUsage,
MemoryUsage::getUsed)));
}
observables.add(
meter
.upDownCounterBuilder("jvm.memory.used")
.setDescription("Measure of memory used.")
.setUnit("By")
.buildWithCallback(
callback(
JVM_MEMORY_POOL_NAME,
JVM_MEMORY_TYPE,
poolBeans,
MemoryPoolMXBean::getUsage,
MemoryUsage::getUsed)));
observables.add(
meter
.upDownCounterBuilder("jvm.memory.committed")
.setDescription("Measure of memory committed.")
.setUnit("By")
.buildWithCallback(
callback(
JVM_MEMORY_POOL_NAME,
JVM_MEMORY_TYPE,
poolBeans,
MemoryPoolMXBean::getUsage,
MemoryUsage::getCommitted)));
observables.add(
meter
.upDownCounterBuilder("jvm.memory.limit")
.setDescription("Measure of max obtainable memory.")
.setUnit("By")
.buildWithCallback(
callback(
JVM_MEMORY_POOL_NAME,
JVM_MEMORY_TYPE,
poolBeans,
MemoryPoolMXBean::getUsage,
MemoryUsage::getMax)));
observables.add(
meter
.upDownCounterBuilder("jvm.memory.used_after_last_gc")
.setDescription(
"Measure of memory used, as measured after the most recent garbage collection event on this pool.")
.setUnit("By")
.buildWithCallback(
callback(
JVM_MEMORY_POOL_NAME,
JVM_MEMORY_TYPE,
poolBeans,
MemoryPoolMXBean::getCollectionUsage,
MemoryUsage::getUsed)));
return observables;
}

View File

@ -14,7 +14,6 @@ import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.ObservableLongMeasurement;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsUtil;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@ -31,7 +30,10 @@ import java.util.function.Consumer;
import javax.annotation.Nullable;
/**
* Registers measurements that generate metrics about JVM threads.
* Registers measurements that generate metrics about JVM threads. The metrics generated by this
* class follow <a
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/docs/runtime/jvm-metrics.md">the
* stable JVM metrics semantic conventions</a>.
*
* <p>Example usage:
*
@ -42,18 +44,6 @@ import javax.annotation.Nullable;
* <p>Example metrics being exported:
*
* <pre>
* process.runtime.jvm.threads.count{daemon=true} 2
* process.runtime.jvm.threads.count{daemon=false} 5
* </pre>
*
* <p>In case you enable the preview of stable JVM semantic conventions (e.g. by setting the {@code
* otel.semconv-stability.opt-in} system property to {@code jvm}), the metrics being exported will
* follow <a
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/docs/runtime/jvm-metrics.md">the
* most recent JVM semantic conventions</a>. This is how the example above looks when stable JVM
* semconv is enabled:
*
* <pre>
* jvm.thread.count{jvm.thread.daemon=true,jvm.thread.state="waiting"} 1
* jvm.thread.count{jvm.thread.daemon=true,jvm.thread.state="runnable"} 2
* jvm.thread.count{jvm.thread.daemon=false,jvm.thread.state="waiting"} 2
@ -65,8 +55,6 @@ public final class Threads {
// Visible for testing
static final Threads INSTANCE = new Threads();
static final AttributeKey<Boolean> DAEMON = booleanKey("daemon");
// TODO: use the opentelemetry-semconv classes once we have metrics attributes there
static final AttributeKey<Boolean> JVM_THREAD_DAEMON = booleanKey("jvm.thread.daemon");
static final AttributeKey<String> JVM_THREAD_STATE = stringKey("jvm.thread.state");
@ -81,34 +69,13 @@ public final class Threads {
Meter meter = JmxRuntimeMetricsUtil.getMeter(openTelemetry);
List<AutoCloseable> observables = new ArrayList<>();
if (SemconvStability.emitOldJvmSemconv()) {
observables.add(
meter
.upDownCounterBuilder("process.runtime.jvm.threads.count")
.setDescription("Number of executing threads")
.setUnit("{thread}")
.buildWithCallback(
observableMeasurement -> {
int daemonThreadCount = threadBean.getDaemonThreadCount();
observableMeasurement.record(
daemonThreadCount, Attributes.builder().put(DAEMON, true).build());
observableMeasurement.record(
threadBean.getThreadCount() - daemonThreadCount,
Attributes.builder().put(DAEMON, false).build());
}));
}
if (SemconvStability.emitStableJvmSemconv()) {
observables.add(
meter
.upDownCounterBuilder("jvm.thread.count")
.setDescription("Number of executing platform threads.")
.setUnit("{thread}")
.buildWithCallback(
isJava9OrNewer()
? java9AndNewerCallback(threadBean)
: java8Callback(threadBean)));
}
observables.add(
meter
.upDownCounterBuilder("jvm.thread.count")
.setDescription("Number of executing platform threads.")
.setUnit("{thread}")
.buildWithCallback(
isJava9OrNewer() ? java9AndNewerCallback(threadBean) : java8Callback(threadBean)));
return observables;
}

View File

@ -12,11 +12,9 @@ import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.ObservableLongMeasurement;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import java.lang.management.BufferPoolMXBean;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
@ -45,9 +43,6 @@ public final class ExperimentalBufferPools {
static List<AutoCloseable> registerObservers(
OpenTelemetry openTelemetry, List<BufferPoolMXBean> bufferBeans) {
if (!SemconvStability.emitStableJvmSemconv()) {
return Collections.emptyList();
}
List<AutoCloseable> observables = new ArrayList<>();
Meter meter = JmxRuntimeMetricsUtil.getMeter(openTelemetry);
observables.add(

View File

@ -5,11 +5,8 @@
package io.opentelemetry.instrumentation.runtimemetrics.java8.internal;
import static java.util.Collections.emptyList;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.util.ArrayList;
@ -41,9 +38,6 @@ public final class ExperimentalCpu {
OperatingSystemMXBean osBean,
@Nullable Supplier<Double> systemCpuUtilization) {
if (!SemconvStability.emitStableJvmSemconv()) {
return emptyList();
}
Meter meter = JmxRuntimeMetricsUtil.getMeter(openTelemetry);
List<AutoCloseable> observables = new ArrayList<>();
observables.add(

View File

@ -6,7 +6,6 @@
package io.opentelemetry.instrumentation.runtimemetrics.java8.internal;
import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import io.opentelemetry.api.OpenTelemetry;
@ -14,7 +13,6 @@ import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.ObservableLongMeasurement;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryType;
@ -50,10 +48,6 @@ public final class ExperimentalMemoryPools {
static List<AutoCloseable> registerObservers(
OpenTelemetry openTelemetry, List<MemoryPoolMXBean> poolBeans) {
if (!SemconvStability.emitStableJvmSemconv()) {
return emptyList();
}
Meter meter = JmxRuntimeMetricsUtil.getMeter(openTelemetry);
return singletonList(
meter

View File

@ -1,134 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.runtimemetrics.java8;
import static io.opentelemetry.instrumentation.runtimemetrics.java8.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;
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)
class BufferPoolsTest {
@RegisterExtension
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
@Spy private ObservableLongMeasurement measurement;
@Mock private BufferPoolMXBean bufferPoolBean;
private List<BufferPoolMXBean> beans;
@BeforeEach
void setup() {
when(bufferPoolBean.getName()).thenReturn("buffer_pool_1");
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-telemetry-java8",
"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-telemetry-java8",
"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-telemetry-java8",
"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);
Consumer<ObservableLongMeasurement> callback =
BufferPools.callback(beans, BufferPoolMXBean::getMemoryUsed);
callback.accept(measurement);
verify(measurement).record(1, Attributes.builder().put("pool", "buffer_pool_1").build());
}
@Test
void callback_SkipRecord() {
when(bufferPoolBean.getMemoryUsed()).thenReturn(-1L);
Consumer<ObservableLongMeasurement> callback =
BufferPools.callback(beans, BufferPoolMXBean::getMemoryUsed);
callback.accept(measurement);
verify(measurement, never()).record(eq(-1), any());
}
}

View File

@ -1,87 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.runtimemetrics.java8;
import static io.opentelemetry.instrumentation.runtimemetrics.java8.ScopeUtil.EXPECTED_SCOPE;
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.ClassLoadingMXBean;
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 ClassesTest {
@RegisterExtension
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
@Mock private ClassLoadingMXBean classBean;
@Test
void registerObservers() {
when(classBean.getTotalLoadedClassCount()).thenReturn(3L);
when(classBean.getUnloadedClassCount()).thenReturn(2L);
when(classBean.getLoadedClassCount()).thenReturn(1);
Classes.INSTANCE.registerObservers(testing.getOpenTelemetry(), classBean);
testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-telemetry-java8",
"process.runtime.jvm.classes.loaded",
metrics ->
metrics.anySatisfy(
metricData ->
assertThat(metricData)
.hasInstrumentationScope(EXPECTED_SCOPE)
.hasDescription("Number of classes loaded since JVM start")
.hasUnit("{class}")
.hasLongSumSatisfying(
sum ->
sum.isMonotonic()
.hasPointsSatisfying(
point ->
point.hasValue(3).hasAttributes(Attributes.empty())))));
testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-telemetry-java8",
"process.runtime.jvm.classes.unloaded",
metrics ->
metrics.anySatisfy(
metricData ->
assertThat(metricData)
.hasInstrumentationScope(EXPECTED_SCOPE)
.hasDescription("Number of classes unloaded since JVM start")
.hasUnit("{class}")
.hasLongSumSatisfying(
sum ->
sum.isMonotonic()
.hasPointsSatisfying(
point ->
point.hasValue(2).hasAttributes(Attributes.empty())))));
testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-telemetry-java8",
"process.runtime.jvm.classes.current_loaded",
metrics ->
metrics.anySatisfy(
metricData ->
assertThat(metricData)
.hasInstrumentationScope(EXPECTED_SCOPE)
.hasDescription("Number of classes currently loaded")
.hasUnit("{class}")
.hasLongSumSatisfying(
sum ->
sum.isNotMonotonic()
.hasPointsSatisfying(
point ->
point.hasValue(1).hasAttributes(Attributes.empty())))));
}
}

View File

@ -28,12 +28,7 @@ class CpuStableSemconvTest {
Supplier<Double> processCpuUtilization = () -> 0.05;
Cpu.INSTANCE.registerObservers(
testing.getOpenTelemetry(),
null,
availableProcessors,
processCpuTime,
() -> null,
processCpuUtilization);
testing.getOpenTelemetry(), availableProcessors, processCpuTime, processCpuUtilization);
testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-telemetry-java8",

View File

@ -1,76 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.runtimemetrics.java8;
import static io.opentelemetry.instrumentation.runtimemetrics.java8.ScopeUtil.EXPECTED_SCOPE;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.mockito.Mockito.when;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
import java.lang.management.OperatingSystemMXBean;
import java.util.function.Supplier;
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 CpuTest {
@RegisterExtension
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
@Mock private OperatingSystemMXBean osBean;
@Test
void registerObservers() {
when(osBean.getSystemLoadAverage()).thenReturn(2.2);
Supplier<Double> systemCpuUsage = () -> 0.11;
Supplier<Double> processCpuUsage = () -> 0.05;
Cpu.INSTANCE.registerObservers(
testing.getOpenTelemetry(), osBean, () -> 0, () -> null, systemCpuUsage, processCpuUsage);
testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-telemetry-java8",
"process.runtime.jvm.system.cpu.load_1m",
metrics ->
metrics.anySatisfy(
metricData ->
assertThat(metricData)
.hasInstrumentationScope(EXPECTED_SCOPE)
.hasDescription("Average CPU load of the whole system for the last minute")
.hasUnit("{run_queue_item}")
.hasDoubleGaugeSatisfying(
gauge -> gauge.hasPointsSatisfying(point -> point.hasValue(2.2)))));
testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-telemetry-java8",
"process.runtime.jvm.system.cpu.utilization",
metrics ->
metrics.anySatisfy(
metricData ->
assertThat(metricData)
.hasInstrumentationScope(EXPECTED_SCOPE)
.hasDescription("Recent cpu utilization for the whole system")
.hasUnit("1")
.hasDoubleGaugeSatisfying(
gauge -> gauge.hasPointsSatisfying(point -> point.hasValue(0.11)))));
testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-telemetry-java8",
"process.runtime.jvm.cpu.utilization",
metrics ->
metrics.anySatisfy(
metricData ->
assertThat(metricData)
.hasInstrumentationScope(EXPECTED_SCOPE)
.hasDescription("Recent cpu utilization for the process")
.hasUnit("1")
.hasDoubleGaugeSatisfying(
gauge -> gauge.hasPointsSatisfying(point -> point.hasValue(0.05)))));
}
}

View File

@ -7,6 +7,7 @@ package io.opentelemetry.instrumentation.runtimemetrics.java8;
import static io.opentelemetry.instrumentation.runtimemetrics.java8.ScopeUtil.EXPECTED_SCOPE;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static java.util.Collections.singletonList;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@ -18,7 +19,6 @@ import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
import java.lang.management.GarbageCollectorMXBean;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicLong;
import javax.management.Notification;
import javax.management.NotificationEmitter;
@ -37,6 +37,9 @@ import org.mockito.quality.Strictness;
@MockitoSettings(strictness = Strictness.LENIENT)
class GarbageCollectorTest {
static final double[] GC_DURATION_BUCKETS =
GarbageCollector.GC_DURATION_BUCKETS.stream().mapToDouble(d -> d).toArray();
@RegisterExtension
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
@ -49,7 +52,7 @@ class GarbageCollectorTest {
void registerObservers() {
GarbageCollector.registerObservers(
testing.getOpenTelemetry(),
Collections.singletonList(gcBean),
singletonList(gcBean),
GarbageCollectorTest::getGcNotificationInfo);
NotificationEmitter notificationEmitter = (NotificationEmitter) gcBean;
@ -65,13 +68,13 @@ class GarbageCollectorTest {
testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-telemetry-java8",
"process.runtime.jvm.gc.duration",
"jvm.gc.duration",
metrics ->
metrics.anySatisfy(
metricData ->
assertThat(metricData)
.hasInstrumentationScope(EXPECTED_SCOPE)
.hasDescription("Duration of JVM garbage collection actions")
.hasDescription("Duration of JVM garbage collection actions.")
.hasUnit("s")
.hasHistogramSatisfying(
histogram ->
@ -82,20 +85,20 @@ class GarbageCollectorTest {
.hasSum(0.022)
.hasAttributes(
Attributes.builder()
.put("gc", "G1 Young Generation")
.put("action", "end of minor GC")
.put("jvm.gc.name", "G1 Young Generation")
.put("jvm.gc.action", "end of minor GC")
.build())
.hasBucketBoundaries(),
.hasBucketBoundaries(GC_DURATION_BUCKETS),
point ->
point
.hasCount(1)
.hasSum(0.011)
.hasAttributes(
Attributes.builder()
.put("gc", "G1 Old Generation")
.put("action", "end of major GC")
.put("jvm.gc.name", "G1 Old Generation")
.put("jvm.gc.action", "end of major GC")
.build())
.hasBucketBoundaries()))));
.hasBucketBoundaries(GC_DURATION_BUCKETS)))));
}
private static Notification createTestNotification(

View File

@ -1,257 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.runtimemetrics.java8;
import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static io.opentelemetry.instrumentation.runtimemetrics.java8.ScopeUtil.EXPECTED_SCOPE;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static java.util.Collections.singletonList;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
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.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;
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.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class MemoryPoolsTest {
@RegisterExtension
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
@Spy private ObservableLongMeasurement measurement;
@Mock private MemoryPoolMXBean heapPoolBean;
@Mock private MemoryPoolMXBean nonHeapPoolBean;
@Mock private MemoryUsage heapPoolUsage;
@Mock private MemoryUsage nonHeapUsage;
@Mock private MemoryUsage heapCollectionUsage;
@Mock private MemoryUsage nonHeapCollectionUsage;
private List<MemoryPoolMXBean> beans;
@BeforeEach
void setup() {
when(heapPoolBean.getName()).thenReturn("heap_pool");
when(heapPoolBean.getType()).thenReturn(MemoryType.HEAP);
when(heapPoolBean.getUsage()).thenReturn(heapPoolUsage);
when(heapPoolBean.getCollectionUsage()).thenReturn(heapCollectionUsage);
when(nonHeapPoolBean.getName()).thenReturn("non_heap_pool");
when(nonHeapPoolBean.getType()).thenReturn(MemoryType.NON_HEAP);
when(nonHeapPoolBean.getUsage()).thenReturn(nonHeapUsage);
when(nonHeapPoolBean.getCollectionUsage()).thenReturn(nonHeapCollectionUsage);
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);
when(heapCollectionUsage.getUsed()).thenReturn(18L);
when(nonHeapCollectionUsage.getUsed()).thenReturn(19L);
MemoryPools.registerObservers(testing.getOpenTelemetry(), beans);
testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-telemetry-java8",
"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(stringKey("pool"), "heap_pool")
.hasAttribute(stringKey("type"), "heap"),
point ->
point
.hasValue(14)
.hasAttribute(stringKey("pool"), "non_heap_pool")
.hasAttribute(stringKey("type"), "non_heap")))));
testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-telemetry-java8",
"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(stringKey("pool"), "heap_pool")
.hasAttribute(stringKey("type"), "heap"),
point ->
point
.hasValue(15)
.hasAttribute(stringKey("pool"), "non_heap_pool")
.hasAttribute(stringKey("type"), "non_heap")))));
testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-telemetry-java8",
"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(stringKey("pool"), "heap_pool")
.hasAttribute(stringKey("type"), "heap"),
point ->
point
.hasValue(16)
.hasAttribute(stringKey("pool"), "non_heap_pool")
.hasAttribute(stringKey("type"), "non_heap")))));
testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-telemetry-java8",
"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(stringKey("pool"), "heap_pool")
.hasAttribute(stringKey("type"), "heap"),
point ->
point
.hasValue(17)
.hasAttribute(stringKey("pool"), "non_heap_pool")
.hasAttribute(stringKey("type"), "non_heap")))));
testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-telemetry-java8",
"process.runtime.jvm.memory.usage_after_last_gc",
metrics ->
metrics.anySatisfy(
metricData ->
assertThat(metricData)
.hasInstrumentationScope(EXPECTED_SCOPE)
.hasDescription(
"Measure of memory used after the most recent garbage collection event on this pool")
.hasUnit("By")
.hasLongSumSatisfying(
sum ->
sum.hasPointsSatisfying(
point ->
point
.hasValue(18)
.hasAttribute(stringKey("pool"), "heap_pool")
.hasAttribute(stringKey("type"), "heap"),
point ->
point
.hasValue(19)
.hasAttribute(stringKey("pool"), "non_heap_pool")
.hasAttribute(stringKey("type"), "non_heap")))));
}
@Test
void callback_Records() {
when(heapPoolUsage.getUsed()).thenReturn(1L);
when(nonHeapUsage.getUsed()).thenReturn(2L);
Consumer<ObservableLongMeasurement> callback =
MemoryPools.callback(
stringKey("pool"),
stringKey("type"),
beans,
MemoryPoolMXBean::getUsage,
MemoryUsage::getUsed);
callback.accept(measurement);
verify(measurement)
.record(1, Attributes.builder().put("pool", "heap_pool").put("type", "heap").build());
verify(measurement)
.record(
2, Attributes.builder().put("pool", "non_heap_pool").put("type", "non_heap").build());
}
@Test
void callback_SkipRecord() {
when(heapPoolUsage.getMax()).thenReturn(1L);
when(nonHeapUsage.getMax()).thenReturn(-1L);
Consumer<ObservableLongMeasurement> callback =
MemoryPools.callback(
stringKey("pool"),
stringKey("type"),
beans,
MemoryPoolMXBean::getUsage,
MemoryUsage::getMax);
callback.accept(measurement);
verify(measurement)
.record(1, Attributes.builder().put("pool", "heap_pool").put("type", "heap").build());
verify(measurement, never()).record(eq(-1), any());
}
@Test
void callback_NullUsage() {
when(heapPoolBean.getCollectionUsage()).thenReturn(null);
Consumer<ObservableLongMeasurement> callback =
MemoryPools.callback(
stringKey("pool"),
stringKey("type"),
singletonList(heapPoolBean),
MemoryPoolMXBean::getCollectionUsage,
MemoryUsage::getUsed);
callback.accept(measurement);
verify(measurement, never()).record(anyLong(), any());
}
}

View File

@ -10,14 +10,15 @@ import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.common.InstrumentationScopeInfoBuilder;
import javax.annotation.Nullable;
class ScopeUtil {
public final class ScopeUtil {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.runtime-telemetry-java8";
@Nullable
private static final String INSTRUMENTATION_VERSION =
EmbeddedInstrumentationProperties.findVersion(INSTRUMENTATION_NAME);
static final InstrumentationScopeInfo EXPECTED_SCOPE;
public static final InstrumentationScopeInfo EXPECTED_SCOPE;
static {
InstrumentationScopeInfoBuilder builder =

View File

@ -1,62 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.runtimemetrics.java8;
import static io.opentelemetry.instrumentation.runtimemetrics.java8.ScopeUtil.EXPECTED_SCOPE;
import static io.opentelemetry.instrumentation.runtimemetrics.java8.Threads.DAEMON;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static org.mockito.Mockito.when;
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(7);
when(threadBean.getDaemonThreadCount()).thenReturn(2);
Threads.INSTANCE.registerObservers(testing.getOpenTelemetry(), threadBean);
testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-telemetry-java8",
"process.runtime.jvm.threads.count",
metrics ->
metrics.anySatisfy(
metricData ->
assertThat(metricData)
.hasInstrumentationScope(EXPECTED_SCOPE)
.hasDescription("Number of executing threads")
.hasUnit("{thread}")
.hasLongSumSatisfying(
sum ->
sum.isNotMonotonic()
.hasPointsSatisfying(
point ->
point
.hasValue(2)
.hasAttributesSatisfying(equalTo(DAEMON, true)),
point ->
point
.hasValue(5)
.hasAttributesSatisfying(
equalTo(DAEMON, false))))));
}
}

View File

@ -1,139 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.runtimemetrics.java8;
import static io.opentelemetry.instrumentation.runtimemetrics.java8.ScopeUtil.EXPECTED_SCOPE;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static java.util.Collections.singletonList;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.sun.management.GarbageCollectionNotificationInfo;
import com.sun.management.GcInfo;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
import java.lang.management.GarbageCollectorMXBean;
import java.util.concurrent.atomic.AtomicLong;
import javax.management.Notification;
import javax.management.NotificationEmitter;
import javax.management.NotificationListener;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class GarbageCollectorTest {
static final double[] GC_DURATION_BUCKETS =
GarbageCollector.GC_DURATION_BUCKETS.stream().mapToDouble(d -> d).toArray();
@RegisterExtension
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
@Mock(extraInterfaces = NotificationEmitter.class)
private GarbageCollectorMXBean gcBean;
@Captor private ArgumentCaptor<NotificationListener> listenerCaptor;
@Test
void registerObservers() {
GarbageCollector.registerObservers(
testing.getOpenTelemetry(),
singletonList(gcBean),
GarbageCollectorTest::getGcNotificationInfo);
NotificationEmitter notificationEmitter = (NotificationEmitter) gcBean;
verify(notificationEmitter).addNotificationListener(listenerCaptor.capture(), any(), any());
NotificationListener listener = listenerCaptor.getValue();
listener.handleNotification(
createTestNotification("G1 Young Generation", "end of minor GC", 10), null);
listener.handleNotification(
createTestNotification("G1 Young Generation", "end of minor GC", 12), null);
listener.handleNotification(
createTestNotification("G1 Old Generation", "end of major GC", 11), null);
testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-telemetry-java8",
"jvm.gc.duration",
metrics ->
metrics.anySatisfy(
metricData ->
assertThat(metricData)
.hasInstrumentationScope(EXPECTED_SCOPE)
.hasDescription("Duration of JVM garbage collection actions.")
.hasUnit("s")
.hasHistogramSatisfying(
histogram ->
histogram.hasPointsSatisfying(
point ->
point
.hasCount(2)
.hasSum(0.022)
.hasAttributes(
Attributes.builder()
.put("jvm.gc.name", "G1 Young Generation")
.put("jvm.gc.action", "end of minor GC")
.build())
.hasBucketBoundaries(GC_DURATION_BUCKETS),
point ->
point
.hasCount(1)
.hasSum(0.011)
.hasAttributes(
Attributes.builder()
.put("jvm.gc.name", "G1 Old Generation")
.put("jvm.gc.action", "end of major GC")
.build())
.hasBucketBoundaries(GC_DURATION_BUCKETS)))));
}
private static Notification createTestNotification(
String gcName, String gcAction, long duration) {
GarbageCollectionNotificationInfo gcNotificationInfo =
mock(GarbageCollectionNotificationInfo.class);
when(gcNotificationInfo.getGcName()).thenReturn(gcName);
when(gcNotificationInfo.getGcAction()).thenReturn(gcAction);
GcInfo gcInfo = mock(GcInfo.class);
when(gcInfo.getDuration()).thenReturn(duration);
when(gcNotificationInfo.getGcInfo()).thenReturn(gcInfo);
return new TestNotification(gcNotificationInfo);
}
private static GarbageCollectionNotificationInfo getGcNotificationInfo(
Notification notification) {
return ((TestNotification) notification).gcNotificationInfo;
}
/**
* A {@link Notification} when is initialized with a mock {@link
* GarbageCollectionNotificationInfo}.
*/
private static class TestNotification extends Notification {
private static final AtomicLong sequence = new AtomicLong(0);
private final GarbageCollectionNotificationInfo gcNotificationInfo;
private TestNotification(GarbageCollectionNotificationInfo gcNotificationInfo) {
super(
GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION,
"test",
sequence.incrementAndGet());
this.gcNotificationInfo = gcNotificationInfo;
}
}
}

View File

@ -1,33 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.runtimemetrics.java8;
import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.common.InstrumentationScopeInfoBuilder;
import javax.annotation.Nullable;
public final class ScopeUtil {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.runtime-telemetry-java8";
@Nullable
private static final String INSTRUMENTATION_VERSION =
EmbeddedInstrumentationProperties.findVersion(INSTRUMENTATION_NAME);
public static final InstrumentationScopeInfo EXPECTED_SCOPE;
static {
InstrumentationScopeInfoBuilder builder =
InstrumentationScopeInfo.builder(INSTRUMENTATION_NAME);
if (INSTRUMENTATION_VERSION != null) {
builder.setVersion(INSTRUMENTATION_VERSION);
}
EXPECTED_SCOPE = builder.build();
}
private ScopeUtil() {}
}

View File

@ -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("process_runtime_jvm_memory_usage")
prometheusData.contains("jvm_memory_used")
cleanup:
stopTarget()

View File

@ -82,10 +82,10 @@ class SpringBootSmokeTest extends SmokeTest {
then: "JVM metrics are exported"
def metrics = new MetricsInspector(waitForMetrics())
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.limit")
metrics.hasMetricsNamed("jvm.memory.used")
metrics.hasMetricsNamed("jvm.memory.committed")
metrics.hasMetricsNamed("jvm.memory.limit")
metrics.hasMetricsNamed("jvm.memory.used_after_last_gc")
then: "service name is autodetected"
def serviceName = findResourceAttribute(traces, "service.name")