Skip to content

Commit 9101f03

Browse files
Spring boot runtime metrics native reduced (#13173)
Co-authored-by: Jean Bisutti <[email protected]>
1 parent 0e2cc4f commit 9101f03

File tree

27 files changed

+560
-117
lines changed

27 files changed

+560
-117
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
Args=\
2-
--initialize-at-build-time=io.opentelemetry.instrumentation.api.internal.cache.concurrentlinkedhashmap.ConcurrentLinkedHashMap
2+
--initialize-at-build-time=io.opentelemetry.instrumentation.api.internal.cache.concurrentlinkedhashmap.ConcurrentLinkedHashMap \
3+
--initialize-at-build-time=io.opentelemetry.instrumentation.api.internal.cache.MapBackedCache \
4+
--initialize-at-build-time=io.opentelemetry.api.internal.InternalAttributeKeyImpl

instrumentation/runtime-telemetry/runtime-telemetry-java17/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java17/Java17RuntimeMetricsInstaller.java

+9-31
Original file line numberDiff line numberDiff line change
@@ -7,47 +7,25 @@
77

88
import com.google.auto.service.AutoService;
99
import io.opentelemetry.api.GlobalOpenTelemetry;
10-
import io.opentelemetry.api.OpenTelemetry;
1110
import io.opentelemetry.instrumentation.runtimemetrics.java17.RuntimeMetrics;
12-
import io.opentelemetry.instrumentation.runtimemetrics.java17.RuntimeMetricsBuilder;
11+
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.RuntimeMetricsConfigUtil;
12+
import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig;
1313
import io.opentelemetry.javaagent.extension.AgentListener;
1414
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
15-
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
1615

1716
/** An {@link AgentListener} that enables runtime metrics during agent startup. */
1817
@AutoService(AgentListener.class)
1918
public class Java17RuntimeMetricsInstaller implements AgentListener {
2019

2120
@Override
2221
public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) {
23-
ConfigProperties config = AgentListener.resolveConfigProperties(autoConfiguredSdk);
24-
25-
OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
26-
RuntimeMetricsBuilder builder = null;
27-
/*
28-
By default don't use any JFR metrics. May change this once semantic conventions are updated.
29-
If enabled, default to only the metrics not already covered by runtime-telemetry-java8
30-
*/
31-
boolean defaultEnabled = config.getBoolean("otel.instrumentation.common.default-enabled", true);
32-
if (config.getBoolean("otel.instrumentation.runtime-telemetry-java17.enable-all", false)) {
33-
builder = RuntimeMetrics.builder(openTelemetry).enableAllFeatures();
34-
} else if (config.getBoolean("otel.instrumentation.runtime-telemetry-java17.enabled", false)) {
35-
builder = RuntimeMetrics.builder(openTelemetry);
36-
} else if (config.getBoolean(
37-
"otel.instrumentation.runtime-telemetry.enabled", defaultEnabled)) {
38-
// This only uses metrics gathered by JMX
39-
builder = RuntimeMetrics.builder(openTelemetry).disableAllFeatures();
40-
}
41-
42-
if (builder != null) {
43-
if (config.getBoolean(
44-
"otel.instrumentation.runtime-telemetry.emit-experimental-telemetry", false)) {
45-
builder.enableExperimentalJmxTelemetry();
46-
}
47-
48-
RuntimeMetrics finalJfrTelemetry = builder.build();
49-
Thread cleanupTelemetry = new Thread(() -> finalJfrTelemetry.close());
50-
Runtime.getRuntime().addShutdownHook(cleanupTelemetry);
22+
RuntimeMetrics runtimeMetrics =
23+
RuntimeMetricsConfigUtil.configure(
24+
RuntimeMetrics.builder(GlobalOpenTelemetry.get()), AgentInstrumentationConfig.get());
25+
if (runtimeMetrics != null) {
26+
Runtime.getRuntime()
27+
.addShutdownHook(
28+
new Thread(runtimeMetrics::close, "OpenTelemetry RuntimeMetricsShutdownHook"));
5129
}
5230
}
5331
}

instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/RuntimeMetrics.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import jdk.jfr.consumer.RecordingStream;
2222

2323
/** The entry point class for runtime metrics support using JFR and JMX. */
24-
public final class RuntimeMetrics implements Closeable {
24+
public final class RuntimeMetrics implements AutoCloseable {
2525

2626
private static final Logger logger = Logger.getLogger(RuntimeMetrics.class.getName());
2727

@@ -101,7 +101,7 @@ private JfrRuntimeMetrics(OpenTelemetry openTelemetry, Predicate<JfrFeature> fea
101101
recordingStream.onEvent(handler.getEventName(), handler);
102102
});
103103
recordingStream.onMetadata(event -> startUpLatch.countDown());
104-
Thread daemonRunner = new Thread(() -> recordingStream.start());
104+
Thread daemonRunner = new Thread(recordingStream::start, "OpenTelemetry JFR-Metrics-Runner");
105105
daemonRunner.setDaemon(true);
106106
daemonRunner.start();
107107
}
@@ -138,7 +138,8 @@ CountDownLatch getStartUpLatch() {
138138
private static boolean isJfrAvailable() {
139139
try {
140140
Class.forName("jdk.jfr.FlightRecorder");
141-
} catch (ClassNotFoundException e) {
141+
// UnsatisfiedLinkError or ClassNotFoundException
142+
} catch (Exception e) {
142143
return false;
143144
}
144145

instrumentation/runtime-telemetry/runtime-telemetry-java17/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java17/RuntimeMetricsBuilder.java

+7-36
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,8 @@
77

88
import com.google.errorprone.annotations.CanIgnoreReturnValue;
99
import io.opentelemetry.api.OpenTelemetry;
10-
import io.opentelemetry.instrumentation.runtimemetrics.java8.Classes;
11-
import io.opentelemetry.instrumentation.runtimemetrics.java8.Cpu;
12-
import io.opentelemetry.instrumentation.runtimemetrics.java8.GarbageCollector;
13-
import io.opentelemetry.instrumentation.runtimemetrics.java8.MemoryPools;
14-
import io.opentelemetry.instrumentation.runtimemetrics.java8.Threads;
15-
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalBufferPools;
16-
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalCpu;
17-
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalMemoryPools;
18-
import java.util.ArrayList;
10+
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsFactory;
1911
import java.util.Arrays;
20-
import java.util.Collections;
2112
import java.util.EnumMap;
2213
import java.util.List;
2314
import javax.annotation.Nullable;
@@ -83,7 +74,7 @@ public RuntimeMetricsBuilder disableAllJmx() {
8374
return this;
8475
}
8576

86-
/** Disable telemetry collection associated with the {@link JfrFeature}. */
77+
/** Enable experimental JMX telemetry collection. */
8778
@CanIgnoreReturnValue
8879
public RuntimeMetricsBuilder enableExperimentalJmxTelemetry() {
8980
enableExperimentalJmxTelemetry = true;
@@ -92,35 +83,15 @@ public RuntimeMetricsBuilder enableExperimentalJmxTelemetry() {
9283

9384
/** Build and start an {@link RuntimeMetrics} with the config from this builder. */
9485
public RuntimeMetrics build() {
95-
List<AutoCloseable> observables = buildObservables();
86+
List<AutoCloseable> observables =
87+
disableJmx
88+
? List.of()
89+
: JmxRuntimeMetricsFactory.buildObservables(
90+
openTelemetry, enableExperimentalJmxTelemetry);
9691
RuntimeMetrics.JfrRuntimeMetrics jfrRuntimeMetrics = buildJfrMetrics();
9792
return new RuntimeMetrics(openTelemetry, observables, jfrRuntimeMetrics);
9893
}
9994

100-
@SuppressWarnings("CatchingUnchecked")
101-
private List<AutoCloseable> buildObservables() {
102-
if (disableJmx) {
103-
return Collections.emptyList();
104-
}
105-
try {
106-
// Set up metrics gathered by JMX
107-
List<AutoCloseable> observables = new ArrayList<>();
108-
observables.addAll(Classes.registerObservers(openTelemetry));
109-
observables.addAll(Cpu.registerObservers(openTelemetry));
110-
observables.addAll(GarbageCollector.registerObservers(openTelemetry));
111-
observables.addAll(MemoryPools.registerObservers(openTelemetry));
112-
observables.addAll(Threads.registerObservers(openTelemetry));
113-
if (enableExperimentalJmxTelemetry) {
114-
observables.addAll(ExperimentalBufferPools.registerObservers(openTelemetry));
115-
observables.addAll(ExperimentalCpu.registerObservers(openTelemetry));
116-
observables.addAll(ExperimentalMemoryPools.registerObservers(openTelemetry));
117-
}
118-
return observables;
119-
} catch (Exception e) {
120-
throw new IllegalStateException("Error building RuntimeMetrics", e);
121-
}
122-
}
123-
12495
@Nullable
12596
private RuntimeMetrics.JfrRuntimeMetrics buildJfrMetrics() {
12697
if (enabledFeatureMap.values().stream().noneMatch(isEnabled -> isEnabled)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.runtimemetrics.java17.internal;
7+
8+
import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig;
9+
import io.opentelemetry.instrumentation.runtimemetrics.java17.RuntimeMetrics;
10+
import io.opentelemetry.instrumentation.runtimemetrics.java17.RuntimeMetricsBuilder;
11+
import javax.annotation.Nullable;
12+
13+
/**
14+
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
15+
* any time.
16+
*/
17+
public final class RuntimeMetricsConfigUtil {
18+
private RuntimeMetricsConfigUtil() {}
19+
20+
@Nullable
21+
public static RuntimeMetrics configure(
22+
RuntimeMetricsBuilder builder, InstrumentationConfig config) {
23+
/*
24+
By default, don't use any JFR metrics. May change this once semantic conventions are updated.
25+
If enabled, default to only the metrics not already covered by runtime-telemetry-java8
26+
*/
27+
boolean defaultEnabled = config.getBoolean("otel.instrumentation.common.default-enabled", true);
28+
if (config.getBoolean("otel.instrumentation.runtime-telemetry-java17.enable-all", false)) {
29+
builder.enableAllFeatures();
30+
} else if (config.getBoolean("otel.instrumentation.runtime-telemetry-java17.enabled", false)) {
31+
// default configuration
32+
} else if (config.getBoolean(
33+
"otel.instrumentation.runtime-telemetry.enabled", defaultEnabled)) {
34+
// This only uses metrics gathered by JMX
35+
builder.disableAllFeatures();
36+
} else {
37+
// nothing is enabled
38+
return null;
39+
}
40+
41+
if (config.getBoolean(
42+
"otel.instrumentation.runtime-telemetry.emit-experimental-telemetry", false)) {
43+
builder.enableExperimentalJmxTelemetry();
44+
}
45+
46+
return builder.build();
47+
}
48+
}

instrumentation/runtime-telemetry/runtime-telemetry-java8/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java8/Java8RuntimeMetricsInstaller.java

+10-37
Original file line numberDiff line numberDiff line change
@@ -7,52 +7,25 @@
77

88
import com.google.auto.service.AutoService;
99
import io.opentelemetry.api.GlobalOpenTelemetry;
10-
import io.opentelemetry.api.OpenTelemetry;
11-
import io.opentelemetry.instrumentation.runtimemetrics.java8.Classes;
12-
import io.opentelemetry.instrumentation.runtimemetrics.java8.Cpu;
13-
import io.opentelemetry.instrumentation.runtimemetrics.java8.GarbageCollector;
14-
import io.opentelemetry.instrumentation.runtimemetrics.java8.MemoryPools;
15-
import io.opentelemetry.instrumentation.runtimemetrics.java8.Threads;
16-
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalBufferPools;
17-
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalCpu;
18-
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalMemoryPools;
19-
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsUtil;
10+
import io.opentelemetry.instrumentation.runtimemetrics.java8.RuntimeMetrics;
11+
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.RuntimeMetricsConfigUtil;
12+
import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig;
2013
import io.opentelemetry.javaagent.extension.AgentListener;
2114
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
22-
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
23-
import java.util.ArrayList;
24-
import java.util.List;
2515

2616
/** An {@link AgentListener} that enables runtime metrics during agent startup. */
2717
@AutoService(AgentListener.class)
2818
public class Java8RuntimeMetricsInstaller implements AgentListener {
2919

3020
@Override
3121
public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) {
32-
ConfigProperties config = AgentListener.resolveConfigProperties(autoConfiguredSdk);
33-
34-
boolean defaultEnabled = config.getBoolean("otel.instrumentation.common.default-enabled", true);
35-
if (!config.getBoolean("otel.instrumentation.runtime-telemetry.enabled", defaultEnabled)
36-
|| Double.parseDouble(System.getProperty("java.specification.version")) >= 17) {
37-
return;
22+
RuntimeMetrics runtimeMetrics =
23+
RuntimeMetricsConfigUtil.configure(
24+
RuntimeMetrics.builder(GlobalOpenTelemetry.get()), AgentInstrumentationConfig.get());
25+
if (runtimeMetrics != null) {
26+
Runtime.getRuntime()
27+
.addShutdownHook(
28+
new Thread(runtimeMetrics::close, "OpenTelemetry RuntimeMetricsShutdownHook"));
3829
}
39-
40-
OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
41-
List<AutoCloseable> observables = new ArrayList<>();
42-
observables.addAll(Classes.registerObservers(openTelemetry));
43-
observables.addAll(Cpu.registerObservers(openTelemetry));
44-
observables.addAll(GarbageCollector.registerObservers(openTelemetry));
45-
observables.addAll(MemoryPools.registerObservers(openTelemetry));
46-
observables.addAll(Threads.registerObservers(openTelemetry));
47-
48-
if (config.getBoolean(
49-
"otel.instrumentation.runtime-telemetry.emit-experimental-telemetry", false)) {
50-
observables.addAll(ExperimentalBufferPools.registerObservers(openTelemetry));
51-
observables.addAll(ExperimentalCpu.registerObservers(openTelemetry));
52-
observables.addAll(ExperimentalMemoryPools.registerObservers(openTelemetry));
53-
}
54-
55-
Thread cleanupTelemetry = new Thread(() -> JmxRuntimeMetricsUtil.closeObservers(observables));
56-
Runtime.getRuntime().addShutdownHook(cleanupTelemetry);
5730
}
5831
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.runtimemetrics.java8;
7+
8+
import io.opentelemetry.api.OpenTelemetry;
9+
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsUtil;
10+
import java.util.Collections;
11+
import java.util.List;
12+
import java.util.concurrent.atomic.AtomicBoolean;
13+
import java.util.logging.Level;
14+
import java.util.logging.Logger;
15+
16+
/** The entry point class for runtime metrics support using JMX. */
17+
public final class RuntimeMetrics implements AutoCloseable {
18+
19+
private static final Logger logger = Logger.getLogger(RuntimeMetrics.class.getName());
20+
21+
private final AtomicBoolean isClosed = new AtomicBoolean();
22+
private final List<AutoCloseable> observables;
23+
24+
RuntimeMetrics(List<AutoCloseable> observables) {
25+
this.observables = Collections.unmodifiableList(observables);
26+
}
27+
28+
/**
29+
* Create and start {@link RuntimeMetrics}.
30+
*
31+
* <p>Listens for select JMX beans, extracts data, and records to various metrics. Recording will
32+
* continue until {@link #close()} is called.
33+
*
34+
* @param openTelemetry the {@link OpenTelemetry} instance used to record telemetry
35+
*/
36+
public static RuntimeMetrics create(OpenTelemetry openTelemetry) {
37+
return new RuntimeMetricsBuilder(openTelemetry).build();
38+
}
39+
40+
/**
41+
* Create a builder for configuring {@link RuntimeMetrics}.
42+
*
43+
* @param openTelemetry the {@link OpenTelemetry} instance used to record telemetry
44+
*/
45+
public static RuntimeMetricsBuilder builder(OpenTelemetry openTelemetry) {
46+
return new RuntimeMetricsBuilder(openTelemetry);
47+
}
48+
49+
/** Stop recording JMX metrics. */
50+
@Override
51+
public void close() {
52+
if (!isClosed.compareAndSet(false, true)) {
53+
logger.log(Level.WARNING, "RuntimeMetrics is already closed");
54+
return;
55+
}
56+
57+
JmxRuntimeMetricsUtil.closeObservers(observables);
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.runtimemetrics.java8;
7+
8+
import com.google.errorprone.annotations.CanIgnoreReturnValue;
9+
import io.opentelemetry.api.OpenTelemetry;
10+
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsFactory;
11+
import java.util.List;
12+
13+
/** Builder for {@link RuntimeMetrics}. */
14+
public final class RuntimeMetricsBuilder {
15+
16+
private final OpenTelemetry openTelemetry;
17+
18+
private boolean enableExperimentalJmxTelemetry = false;
19+
20+
RuntimeMetricsBuilder(OpenTelemetry openTelemetry) {
21+
this.openTelemetry = openTelemetry;
22+
}
23+
24+
/** Enable all JMX telemetry collection. */
25+
@CanIgnoreReturnValue
26+
public RuntimeMetricsBuilder enableExperimentalJmxTelemetry() {
27+
enableExperimentalJmxTelemetry = true;
28+
return this;
29+
}
30+
31+
/** Build and start an {@link RuntimeMetrics} with the config from this builder. */
32+
public RuntimeMetrics build() {
33+
List<AutoCloseable> observables =
34+
JmxRuntimeMetricsFactory.buildObservables(openTelemetry, enableExperimentalJmxTelemetry);
35+
return new RuntimeMetrics(observables);
36+
}
37+
}

instrumentation/runtime-telemetry/runtime-telemetry-java8/library/src/main/java/io/opentelemetry/instrumentation/runtimemetrics/java8/Threads.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public final class Threads {
5757

5858
/** Register observers for java runtime class metrics. */
5959
public static List<AutoCloseable> registerObservers(OpenTelemetry openTelemetry) {
60-
return INSTANCE.registerObservers(openTelemetry, !isJava9OrNewer());
60+
return INSTANCE.registerObservers(openTelemetry, useThreads());
6161
}
6262

6363
private List<AutoCloseable> registerObservers(OpenTelemetry openTelemetry, boolean useThread) {
@@ -116,6 +116,13 @@ private static boolean isJava9OrNewer() {
116116
return THREAD_INFO_IS_DAEMON != null;
117117
}
118118

119+
private static boolean useThreads() {
120+
// GraalVM native image does not support ThreadMXBean yet
121+
// see https://github.com/oracle/graal/issues/6101
122+
boolean isNativeExecution = System.getProperty("org.graalvm.nativeimage.imagecode") != null;
123+
return !isJava9OrNewer() || isNativeExecution;
124+
}
125+
119126
private static Consumer<ObservableLongMeasurement> java8Callback(ThreadMXBean threadBean) {
120127
return measurement -> {
121128
int daemonThreadCount = threadBean.getDaemonThreadCount();

0 commit comments

Comments
 (0)