Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions instrumentation/runtime-telemetry/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Settings for the Runtime Telemetry instrumentation

| System property | Type | Default | Description |
|--------------------------------------------------------------------------|---------|---------|-------------------------------------------------------------------|
| `otel.instrumentation.runtime-telemetry.emit-experimental-telemetry` | Boolean | `false` | Enable the capture of experimental metrics. |
| `otel.instrumentation.runtime-telemetry-java17.enable-all` | Boolean | `false` | Enable the capture of all JFR based metrics. |
| `otel.instrumentation.runtime-telemetry-java17.enabled` | Boolean | `false` | Enable the capture of JFR based metrics. |
| `otel.instrumentation.runtime-telemetry.package-emitter.enabled` | Boolean | `false` | Enable creating events for JAR libraries used by the application. |
| `otel.instrumentation.runtime-telemetry.package-emitter.jars-per-second` | Integer | 10 | The number of JAR files processed per second. |
| System property | Type | Default | Description |
|--------------------------------------------------------------------------|---------|---------|-------------------------------------------------------------------------------------|
| `otel.instrumentation.runtime-telemetry.capture-gc-cause` | Boolean | `false` | Enable the capture of the jvm.gc.cause attribute within the jvm.gc.duration metric.
| `otel.instrumentation.runtime-telemetry.emit-experimental-telemetry` | Boolean | `false` | Enable the capture of experimental metrics. |
| `otel.instrumentation.runtime-telemetry-java17.enable-all` | Boolean | `false` | Enable the capture of all JFR based metrics. |
| `otel.instrumentation.runtime-telemetry-java17.enabled` | Boolean | `false` | Enable the capture of JFR based metrics. |
| `otel.instrumentation.runtime-telemetry.package-emitter.enabled` | Boolean | `false` | Enable creating events for JAR libraries used by the application. |
| `otel.instrumentation.runtime-telemetry.package-emitter.jars-per-second` | Integer | 10 | The number of JAR files processed per second. |
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public final class RuntimeMetricsBuilder {

private boolean disableJmx = false;
private boolean enableExperimentalJmxTelemetry = false;
private boolean enableCaptureGcCause = false;

RuntimeMetricsBuilder(OpenTelemetry openTelemetry) {
this.openTelemetry = openTelemetry;
Expand Down Expand Up @@ -81,13 +82,20 @@ public RuntimeMetricsBuilder enableExperimentalJmxTelemetry() {
return this;
}

/** Enable capture GC cause. */
@CanIgnoreReturnValue
public RuntimeMetricsBuilder enableCaptureGcCause() {
enableCaptureGcCause = true;
return this;
}

/** Build and start an {@link RuntimeMetrics} with the config from this builder. */
public RuntimeMetrics build() {
List<AutoCloseable> observables =
disableJmx
? List.of()
: JmxRuntimeMetricsFactory.buildObservables(
openTelemetry, enableExperimentalJmxTelemetry);
openTelemetry, enableExperimentalJmxTelemetry, enableCaptureGcCause);
RuntimeMetrics.JfrRuntimeMetrics jfrRuntimeMetrics = buildJfrMetrics();
return new RuntimeMetrics(openTelemetry, observables, jfrRuntimeMetrics);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public static RuntimeMetrics configure(
builder.enableExperimentalJmxTelemetry();
}

if (config.getBoolean("otel.instrumentation.runtime-telemetry.capture-gc-cause", false)) {
builder.enableCaptureGcCause();
}

return builder.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import com.sun.management.GarbageCollectionNotificationInfo;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.Meter;
Expand Down Expand Up @@ -62,7 +63,8 @@ public final class GarbageCollector {
.equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION);

/** Register observers for java runtime memory metrics. */
public static List<AutoCloseable> registerObservers(OpenTelemetry openTelemetry) {
public static List<AutoCloseable> registerObservers(
OpenTelemetry openTelemetry, boolean enableCaptureGcCause) {
if (!isNotificationClassPresent()) {
logger.fine(
"The com.sun.management.GarbageCollectionNotificationInfo class is not available;"
Expand All @@ -73,14 +75,16 @@ public static List<AutoCloseable> registerObservers(OpenTelemetry openTelemetry)
return registerObservers(
openTelemetry,
ManagementFactory.getGarbageCollectorMXBeans(),
GarbageCollector::extractNotificationInfo);
GarbageCollector::extractNotificationInfo,
enableCaptureGcCause);
}

// Visible for testing
static List<AutoCloseable> registerObservers(
OpenTelemetry openTelemetry,
List<GarbageCollectorMXBean> gcBeans,
Function<Notification, GarbageCollectionNotificationInfo> notificationInfoExtractor) {
Function<Notification, GarbageCollectionNotificationInfo> notificationInfoExtractor,
boolean captureGcCauseEnabled) {
Meter meter = JmxRuntimeMetricsUtil.getMeter(openTelemetry);

DoubleHistogram gcDuration =
Expand All @@ -98,7 +102,7 @@ static List<AutoCloseable> registerObservers(
}
NotificationEmitter notificationEmitter = (NotificationEmitter) gcBean;
GcNotificationListener listener =
new GcNotificationListener(gcDuration, notificationInfoExtractor);
new GcNotificationListener(gcDuration, notificationInfoExtractor, captureGcCauseEnabled);
notificationEmitter.addNotificationListener(listener, GC_FILTER, null);
result.add(() -> notificationEmitter.removeNotificationListener(listener));
}
Expand All @@ -107,13 +111,16 @@ static List<AutoCloseable> registerObservers(

private static final class GcNotificationListener implements NotificationListener {

private final boolean enableCaptureGcCause;
private final DoubleHistogram gcDuration;
private final Function<Notification, GarbageCollectionNotificationInfo>
notificationInfoExtractor;

private GcNotificationListener(
DoubleHistogram gcDuration,
Function<Notification, GarbageCollectionNotificationInfo> notificationInfoExtractor) {
Function<Notification, GarbageCollectionNotificationInfo> notificationInfoExtractor,
boolean enableCaptureGcCause) {
this.enableCaptureGcCause = enableCaptureGcCause;
this.gcDuration = gcDuration;
this.notificationInfoExtractor = notificationInfoExtractor;
}
Expand All @@ -125,11 +132,20 @@ public void handleNotification(Notification notification, Object unused) {

String gcName = notificationInfo.getGcName();
String gcAction = notificationInfo.getGcAction();
String gcCause = notificationInfo.getGcCause();
double duration = notificationInfo.getGcInfo().getDuration() / MILLIS_PER_S;

gcDuration.record(
duration,
Attributes.of(JvmAttributes.JVM_GC_NAME, gcName, JvmAttributes.JVM_GC_ACTION, gcAction));
Attributes gcAttributes =
enableCaptureGcCause
? Attributes.of(
JvmAttributes.JVM_GC_NAME,
gcName,
JvmAttributes.JVM_GC_ACTION,
gcAction,
AttributeKey.stringKey("jvm.gc.cause"),
gcCause)
: Attributes.of(
JvmAttributes.JVM_GC_NAME, gcName, JvmAttributes.JVM_GC_ACTION, gcAction);
gcDuration.record(duration, gcAttributes);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public final class RuntimeMetricsBuilder {

private boolean enableExperimentalJmxTelemetry = false;

private boolean enableCaptureGcCause = false;

RuntimeMetricsBuilder(OpenTelemetry openTelemetry) {
this.openTelemetry = openTelemetry;
}
Expand All @@ -28,10 +30,18 @@ public RuntimeMetricsBuilder enableExperimentalJmxTelemetry() {
return this;
}

/** Enable capture GC cause. */
@CanIgnoreReturnValue
public RuntimeMetricsBuilder enableCaptureGcCause() {
enableCaptureGcCause = true;
return this;
}

/** Build and start an {@link RuntimeMetrics} with the config from this builder. */
public RuntimeMetrics build() {
List<AutoCloseable> observables =
JmxRuntimeMetricsFactory.buildObservables(openTelemetry, enableExperimentalJmxTelemetry);
JmxRuntimeMetricsFactory.buildObservables(
openTelemetry, enableExperimentalJmxTelemetry, enableCaptureGcCause);
return new RuntimeMetrics(observables);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@
public class JmxRuntimeMetricsFactory {
@SuppressWarnings("CatchingUnchecked")
public static List<AutoCloseable> buildObservables(
OpenTelemetry openTelemetry, boolean enableExperimentalJmxTelemetry) {
OpenTelemetry openTelemetry,
boolean enableExperimentalJmxTelemetry,
boolean enableCaptureGcCause) {
// Set up metrics gathered by JMX
List<AutoCloseable> observables = new ArrayList<>();
observables.addAll(Classes.registerObservers(openTelemetry));
observables.addAll(Cpu.registerObservers(openTelemetry));
observables.addAll(GarbageCollector.registerObservers(openTelemetry));
observables.addAll(GarbageCollector.registerObservers(openTelemetry, enableCaptureGcCause));
observables.addAll(MemoryPools.registerObservers(openTelemetry));
observables.addAll(Threads.registerObservers(openTelemetry));
if (enableExperimentalJmxTelemetry) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public static RuntimeMetrics configure(
builder.enableExperimentalJmxTelemetry();
}

if (config.getBoolean("otel.instrumentation.runtime-telemetry.capture-gc-cause", false)) {
builder.enableCaptureGcCause();
}

return builder.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,21 @@ void registerObservers() {
GarbageCollector.registerObservers(
testing.getOpenTelemetry(),
singletonList(gcBean),
GarbageCollectorTest::getGcNotificationInfo);
GarbageCollectorTest::getGcNotificationInfo,
true);

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);
createTestNotification("G1 Young Generation", "end of minor GC", "Allocation Failure", 10),
null);
listener.handleNotification(
createTestNotification("G1 Young Generation", "end of minor GC", 12), null);
createTestNotification("G1 Young Generation", "end of minor GC", "Allocation Failure", 12),
null);
listener.handleNotification(
createTestNotification("G1 Old Generation", "end of major GC", 11), null);
createTestNotification("G1 Old Generation", "end of major GC", "System.gc()", 11), null);

testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-telemetry-java8",
Expand All @@ -87,6 +90,7 @@ void registerObservers() {
Attributes.builder()
.put("jvm.gc.name", "G1 Young Generation")
.put("jvm.gc.action", "end of minor GC")
.put("jvm.gc.cause", "Allocation Failure")
.build())
.hasBucketBoundaries(GC_DURATION_BUCKETS),
point ->
Expand All @@ -97,16 +101,18 @@ void registerObservers() {
Attributes.builder()
.put("jvm.gc.name", "G1 Old Generation")
.put("jvm.gc.action", "end of major GC")
.put("jvm.gc.cause", "System.gc()")
.build())
.hasBucketBoundaries(GC_DURATION_BUCKETS)))));
}

private static Notification createTestNotification(
String gcName, String gcAction, long duration) {
String gcName, String gcAction, String gcCause, long duration) {
GarbageCollectionNotificationInfo gcNotificationInfo =
mock(GarbageCollectionNotificationInfo.class);
when(gcNotificationInfo.getGcName()).thenReturn(gcName);
when(gcNotificationInfo.getGcAction()).thenReturn(gcAction);
when(gcNotificationInfo.getGcCause()).thenReturn(gcCause);
GcInfo gcInfo = mock(GcInfo.class);
when(gcInfo.getDuration()).thenReturn(duration);
when(gcNotificationInfo.getGcInfo()).thenReturn(gcInfo);
Expand Down
Loading