Skip to content

Commit 5099e45

Browse files
committed
spring boot runtime metrics for java 8
1 parent 4b196cc commit 5099e45

File tree

10 files changed

+198
-94
lines changed

10 files changed

+198
-94
lines changed

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

+1-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
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;
1211
import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig;
1312
import io.opentelemetry.javaagent.extension.AgentListener;
@@ -19,9 +18,7 @@ public class Java17RuntimeMetricsInstaller implements AgentListener {
1918

2019
@Override
2120
public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) {
22-
OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
23-
24-
RuntimeMetrics.builder(openTelemetry)
21+
RuntimeMetrics.builder(GlobalOpenTelemetry.get())
2522
.startFromInstrumentationConfig(AgentInstrumentationConfig.get());
2623
}
2724
}

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

+5-36
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,8 @@
88
import com.google.errorprone.annotations.CanIgnoreReturnValue;
99
import io.opentelemetry.api.OpenTelemetry;
1010
import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig;
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 java.util.ArrayList;
11+
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsFactory;
2012
import java.util.Arrays;
21-
import java.util.Collections;
2213
import java.util.EnumMap;
2314
import java.util.List;
2415
import javax.annotation.Nullable;
@@ -84,7 +75,7 @@ public RuntimeMetricsBuilder disableAllJmx() {
8475
return this;
8576
}
8677

87-
/** Disable telemetry collection associated with the {@link JfrFeature}. */
78+
/** Enable experimental JMX telemetry collection. */
8879
@CanIgnoreReturnValue
8980
public RuntimeMetricsBuilder enableExperimentalJmxTelemetry() {
9081
enableExperimentalJmxTelemetry = true;
@@ -122,35 +113,13 @@ public void startFromInstrumentationConfig(InstrumentationConfig config) {
122113

123114
/** Build and start an {@link RuntimeMetrics} with the config from this builder. */
124115
public RuntimeMetrics build() {
125-
List<AutoCloseable> observables = buildObservables();
116+
List<AutoCloseable> observables =
117+
JmxRuntimeMetricsFactory.buildObservables(
118+
openTelemetry, disableJmx, enableExperimentalJmxTelemetry);
126119
RuntimeMetrics.JfrRuntimeMetrics jfrRuntimeMetrics = buildJfrMetrics();
127120
return new RuntimeMetrics(openTelemetry, observables, jfrRuntimeMetrics);
128121
}
129122

130-
@SuppressWarnings("CatchingUnchecked")
131-
private List<AutoCloseable> buildObservables() {
132-
if (disableJmx) {
133-
return Collections.emptyList();
134-
}
135-
try {
136-
// Set up metrics gathered by JMX
137-
List<AutoCloseable> observables = new ArrayList<>();
138-
observables.addAll(Classes.registerObservers(openTelemetry));
139-
observables.addAll(Cpu.registerObservers(openTelemetry));
140-
observables.addAll(GarbageCollector.registerObservers(openTelemetry));
141-
observables.addAll(MemoryPools.registerObservers(openTelemetry));
142-
observables.addAll(Threads.registerObservers(openTelemetry));
143-
if (enableExperimentalJmxTelemetry) {
144-
observables.addAll(ExperimentalBufferPools.registerObservers(openTelemetry));
145-
observables.addAll(ExperimentalCpu.registerObservers(openTelemetry));
146-
observables.addAll(ExperimentalMemoryPools.registerObservers(openTelemetry));
147-
}
148-
return observables;
149-
} catch (Exception e) {
150-
throw new IllegalStateException("Error building RuntimeMetrics", e);
151-
}
152-
}
153-
154123
@Nullable
155124
private RuntimeMetrics.JfrRuntimeMetrics buildJfrMetrics() {
156125
if (enabledFeatureMap.values().stream().noneMatch(isEnabled -> isEnabled)) {

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

+4-38
Original file line numberDiff line numberDiff line change
@@ -7,52 +7,18 @@
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.javaagent.bootstrap.internal.AgentInstrumentationConfig;
2012
import io.opentelemetry.javaagent.extension.AgentListener;
2113
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
22-
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
23-
import java.util.ArrayList;
24-
import java.util.List;
2514

2615
/** An {@link AgentListener} that enables runtime metrics during agent startup. */
2716
@AutoService(AgentListener.class)
2817
public class Java8RuntimeMetricsInstaller implements AgentListener {
2918

3019
@Override
3120
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;
38-
}
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);
21+
RuntimeMetrics.builder(GlobalOpenTelemetry.get())
22+
.startFromInstrumentationConfig(AgentInstrumentationConfig.get());
5723
}
5824
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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.io.Closeable;
11+
import java.util.Collections;
12+
import java.util.List;
13+
import java.util.concurrent.atomic.AtomicBoolean;
14+
import java.util.logging.Level;
15+
import java.util.logging.Logger;
16+
17+
/** The entry point class for runtime metrics support using JFR and JMX. */
18+
public final class RuntimeMetrics implements Closeable {
19+
20+
private static final Logger logger = Logger.getLogger(RuntimeMetrics.class.getName());
21+
22+
private final AtomicBoolean isClosed = new AtomicBoolean();
23+
private final List<AutoCloseable> observables;
24+
25+
RuntimeMetrics(List<AutoCloseable> observables) {
26+
this.observables = Collections.unmodifiableList(observables);
27+
}
28+
29+
/**
30+
* Create and start {@link RuntimeMetrics}.
31+
*
32+
* <p>Listens for select JFR events, extracts data, and records to various metrics. Recording will
33+
* continue until {@link #close()} is called.
34+
*
35+
* @param openTelemetry the {@link OpenTelemetry} instance used to record telemetry
36+
*/
37+
public static RuntimeMetrics create(OpenTelemetry openTelemetry) {
38+
return new RuntimeMetricsBuilder(openTelemetry).build();
39+
}
40+
41+
/**
42+
* Create a builder for configuring {@link RuntimeMetrics}.
43+
*
44+
* @param openTelemetry the {@link OpenTelemetry} instance used to record telemetry
45+
*/
46+
public static RuntimeMetricsBuilder builder(OpenTelemetry openTelemetry) {
47+
return new RuntimeMetricsBuilder(openTelemetry);
48+
}
49+
50+
/** Stop recording JFR events. */
51+
@Override
52+
public void close() {
53+
if (!isClosed.compareAndSet(false, true)) {
54+
logger.log(Level.WARNING, "RuntimeMetrics is already closed");
55+
return;
56+
}
57+
58+
JmxRuntimeMetricsUtil.closeObservers(observables);
59+
}
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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.api.incubator.config.internal.InstrumentationConfig;
11+
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsFactory;
12+
import java.util.List;
13+
14+
/** Builder for {@link RuntimeMetrics}. */
15+
public final class RuntimeMetricsBuilder {
16+
17+
private final OpenTelemetry openTelemetry;
18+
19+
private boolean disableJmx = false;
20+
private boolean enableExperimentalJmxTelemetry = false;
21+
22+
RuntimeMetricsBuilder(OpenTelemetry openTelemetry) {
23+
this.openTelemetry = openTelemetry;
24+
}
25+
26+
/** Enable all JMX telemetry collection. */
27+
@CanIgnoreReturnValue
28+
public RuntimeMetricsBuilder enableExperimentalJmxTelemetry() {
29+
enableExperimentalJmxTelemetry = true;
30+
return this;
31+
}
32+
33+
/** Build and start an {@link RuntimeMetrics} with the config from this builder. */
34+
public RuntimeMetrics build() {
35+
List<AutoCloseable> observables =
36+
JmxRuntimeMetricsFactory.buildObservables(
37+
openTelemetry, disableJmx, enableExperimentalJmxTelemetry);
38+
return new RuntimeMetrics(observables);
39+
}
40+
41+
public void startFromInstrumentationConfig(InstrumentationConfig config) {
42+
/*
43+
By default, don't use any JFR metrics. May change this once semantic conventions are updated.
44+
If enabled, default to only the metrics not already covered by runtime-telemetry-java8
45+
*/
46+
boolean defaultEnabled = config.getBoolean("otel.instrumentation.common.default-enabled", true);
47+
if (!config.getBoolean("otel.instrumentation.runtime-telemetry.enabled", defaultEnabled)) {
48+
// nothing is enabled
49+
return;
50+
}
51+
52+
if (config.getBoolean(
53+
"otel.instrumentation.runtime-telemetry.emit-experimental-telemetry", false)) {
54+
this.enableExperimentalJmxTelemetry();
55+
}
56+
57+
RuntimeMetrics finalJfrTelemetry = this.build();
58+
Thread cleanupTelemetry = new Thread(finalJfrTelemetry::close);
59+
Runtime.getRuntime().addShutdownHook(cleanupTelemetry);
60+
}
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.runtimemetrics.java8.internal;
7+
8+
import io.opentelemetry.api.OpenTelemetry;
9+
import io.opentelemetry.instrumentation.runtimemetrics.java8.Classes;
10+
import io.opentelemetry.instrumentation.runtimemetrics.java8.Cpu;
11+
import io.opentelemetry.instrumentation.runtimemetrics.java8.GarbageCollector;
12+
import io.opentelemetry.instrumentation.runtimemetrics.java8.MemoryPools;
13+
import io.opentelemetry.instrumentation.runtimemetrics.java8.Threads;
14+
import java.util.ArrayList;
15+
import java.util.Collections;
16+
import java.util.List;
17+
18+
/**
19+
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
20+
* any time.
21+
*/
22+
public class JmxRuntimeMetricsFactory {
23+
@SuppressWarnings("CatchingUnchecked")
24+
public static List<AutoCloseable> buildObservables(
25+
OpenTelemetry openTelemetry, boolean disableJmx, boolean enableExperimentalJmxTelemetry) {
26+
if (disableJmx) {
27+
return Collections.emptyList();
28+
}
29+
try {
30+
// Set up metrics gathered by JMX
31+
List<AutoCloseable> observables = new ArrayList<>();
32+
observables.addAll(Classes.registerObservers(openTelemetry));
33+
observables.addAll(Cpu.registerObservers(openTelemetry));
34+
observables.addAll(GarbageCollector.registerObservers(openTelemetry));
35+
observables.addAll(MemoryPools.registerObservers(openTelemetry));
36+
observables.addAll(Threads.registerObservers(openTelemetry));
37+
if (enableExperimentalJmxTelemetry) {
38+
observables.addAll(ExperimentalBufferPools.registerObservers(openTelemetry));
39+
observables.addAll(ExperimentalCpu.registerObservers(openTelemetry));
40+
observables.addAll(ExperimentalMemoryPools.registerObservers(openTelemetry));
41+
}
42+
return observables;
43+
} catch (Exception e) {
44+
throw new IllegalStateException("Error building RuntimeMetrics", e);
45+
}
46+
}
47+
48+
private JmxRuntimeMetricsFactory() {}
49+
}

instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ dependencies {
5353
implementation(project(":instrumentation:logback:logback-mdc-1.0:library"))
5454
compileOnly("ch.qos.logback:logback-classic:1.0.0")
5555
implementation(project(":instrumentation:jdbc:library"))
56+
implementation(project(":instrumentation:runtime-telemetry:runtime-telemetry-java8:library"))
5657
implementation(project(":instrumentation:runtime-telemetry:runtime-telemetry-java17:library"))
5758

5859
library("org.springframework.kafka:spring-kafka:2.9.0")
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics;
77

88
import io.opentelemetry.api.OpenTelemetry;
9-
import io.opentelemetry.instrumentation.runtimemetrics.java17.RuntimeMetrics;
109
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation;
1110
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.ConfigPropertiesBridge;
1211
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
@@ -23,26 +22,28 @@
2322
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
2423
* at any time.
2524
*/
26-
@ConditionalOnEnabledInstrumentation(module = "runtime-telemetry-java17")
25+
@ConditionalOnEnabledInstrumentation(module = "runtime-telemetry")
2726
@Configuration
28-
public class Java17RuntimeMetricsAutoConfiguration {
27+
public class RuntimeMetricsAutoConfiguration {
2928

3029
private static final Logger logger =
31-
LoggerFactory.getLogger(Java17RuntimeMetricsAutoConfiguration.class);
30+
LoggerFactory.getLogger(RuntimeMetricsAutoConfiguration.class);
3231

3332
@EventListener
3433
public void handleApplicationReadyEvent(ApplicationReadyEvent event) {
35-
if (Double.parseDouble(System.getProperty("java.specification.version")) < 17) {
36-
logger.debug(
37-
"Java 17 runtime metrics instrumentation enabled but running on Java version < 17");
38-
return;
39-
}
40-
logger.debug(
41-
"Java 17 runtime metrics instrumentation enabled and running on Java version >= 17");
4234
ConfigurableApplicationContext applicationContext = event.getApplicationContext();
4335
OpenTelemetry openTelemetry = applicationContext.getBean(OpenTelemetry.class);
44-
ConfigProperties configProperties = applicationContext.getBean(ConfigProperties.class);
45-
RuntimeMetrics.builder(openTelemetry)
46-
.startFromInstrumentationConfig(new ConfigPropertiesBridge(configProperties));
36+
ConfigPropertiesBridge config =
37+
new ConfigPropertiesBridge(applicationContext.getBean(ConfigProperties.class));
38+
39+
if (Double.parseDouble(System.getProperty("java.specification.version")) >= 17) {
40+
logger.debug("Use runtime metrics instrumentation for Java 17+");
41+
io.opentelemetry.instrumentation.runtimemetrics.java17.RuntimeMetrics.builder(openTelemetry)
42+
.startFromInstrumentationConfig(config);
43+
} else {
44+
logger.debug("Use runtime metrics instrumentation for Java 8");
45+
io.opentelemetry.instrumentation.runtimemetrics.java8.RuntimeMetrics.builder(openTelemetry)
46+
.startFromInstrumentationConfig(config);
47+
}
4748
}
4849
}

instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.w
1111
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webflux.SpringWebfluxInstrumentationAutoConfiguration,\
1212
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webmvc.SpringWebMvc5InstrumentationAutoConfiguration,\
1313
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling.SpringSchedulingInstrumentationAutoConfiguration,\
14-
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.Java17RuntimeMetricsAutoConfiguration
14+
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.RuntimeMetricsAutoConfiguration
1515

1616
org.springframework.context.ApplicationListener=\
1717
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.logging.LogbackAppenderApplicationListener

instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.w
1111
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.web.RestClientInstrumentationAutoConfiguration
1212
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webmvc.SpringWebMvc6InstrumentationAutoConfiguration
1313
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling.SpringSchedulingInstrumentationAutoConfiguration
14-
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.Java17RuntimeMetricsAutoConfiguration
14+
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.RuntimeMetricsAutoConfiguration

0 commit comments

Comments
 (0)