Skip to content

Commit 4b196cc

Browse files
committed
spring boot runtime metrics for java 17+
1 parent 5d6ebba commit 4b196cc

File tree

9 files changed

+97
-33
lines changed

9 files changed

+97
-33
lines changed

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

+3-29
Original file line numberDiff line numberDiff line change
@@ -9,45 +9,19 @@
99
import io.opentelemetry.api.GlobalOpenTelemetry;
1010
import io.opentelemetry.api.OpenTelemetry;
1111
import io.opentelemetry.instrumentation.runtimemetrics.java17.RuntimeMetrics;
12-
import io.opentelemetry.instrumentation.runtimemetrics.java17.RuntimeMetricsBuilder;
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-
2522
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-
}
4723

48-
RuntimeMetrics finalJfrTelemetry = builder.build();
49-
Thread cleanupTelemetry = new Thread(() -> finalJfrTelemetry.close());
50-
Runtime.getRuntime().addShutdownHook(cleanupTelemetry);
51-
}
24+
RuntimeMetrics.builder(openTelemetry)
25+
.startFromInstrumentationConfig(AgentInstrumentationConfig.get());
5226
}
5327
}

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

+30
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import com.google.errorprone.annotations.CanIgnoreReturnValue;
99
import io.opentelemetry.api.OpenTelemetry;
10+
import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig;
1011
import io.opentelemetry.instrumentation.runtimemetrics.java8.Classes;
1112
import io.opentelemetry.instrumentation.runtimemetrics.java8.Cpu;
1213
import io.opentelemetry.instrumentation.runtimemetrics.java8.GarbageCollector;
@@ -90,6 +91,35 @@ public RuntimeMetricsBuilder enableExperimentalJmxTelemetry() {
9091
return this;
9192
}
9293

94+
public void startFromInstrumentationConfig(InstrumentationConfig config) {
95+
/*
96+
By default, don't use any JFR metrics. May change this once semantic conventions are updated.
97+
If enabled, default to only the metrics not already covered by runtime-telemetry-java8
98+
*/
99+
boolean defaultEnabled = config.getBoolean("otel.instrumentation.common.default-enabled", true);
100+
if (config.getBoolean("otel.instrumentation.runtime-telemetry-java17.enable-all", false)) {
101+
this.enableAllFeatures();
102+
} else if (config.getBoolean("otel.instrumentation.runtime-telemetry-java17.enabled", false)) {
103+
// default configuration
104+
} else if (config.getBoolean(
105+
"otel.instrumentation.runtime-telemetry.enabled", defaultEnabled)) {
106+
// This only uses metrics gathered by JMX
107+
this.disableAllFeatures();
108+
} else {
109+
// nothing is enabled
110+
return;
111+
}
112+
113+
if (config.getBoolean(
114+
"otel.instrumentation.runtime-telemetry.emit-experimental-telemetry", false)) {
115+
this.enableExperimentalJmxTelemetry();
116+
}
117+
118+
RuntimeMetrics finalJfrTelemetry = this.build();
119+
Thread cleanupTelemetry = new Thread(finalJfrTelemetry::close);
120+
Runtime.getRuntime().addShutdownHook(cleanupTelemetry);
121+
}
122+
93123
/** Build and start an {@link RuntimeMetrics} with the config from this builder. */
94124
public RuntimeMetrics build() {
95125
List<AutoCloseable> observables = buildObservables();

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-java17:library"))
5657

5758
library("org.springframework.kafka:spring-kafka:2.9.0")
5859
library("org.springframework.boot:spring-boot-starter-actuator:$springBootVersion")
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.spring.autoconfigure.internal.instrumentation.runtimemetrics;
7+
8+
import io.opentelemetry.api.OpenTelemetry;
9+
import io.opentelemetry.instrumentation.runtimemetrics.java17.RuntimeMetrics;
10+
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation;
11+
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.ConfigPropertiesBridge;
12+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
13+
import org.slf4j.Logger;
14+
import org.slf4j.LoggerFactory;
15+
import org.springframework.boot.context.event.ApplicationReadyEvent;
16+
import org.springframework.context.ConfigurableApplicationContext;
17+
import org.springframework.context.annotation.Configuration;
18+
import org.springframework.context.event.EventListener;
19+
20+
/**
21+
* Configures runtime metrics collection.
22+
*
23+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
24+
* at any time.
25+
*/
26+
@ConditionalOnEnabledInstrumentation(module = "runtime-telemetry-java17")
27+
@Configuration
28+
public class Java17RuntimeMetricsAutoConfiguration {
29+
30+
private static final Logger logger =
31+
LoggerFactory.getLogger(Java17RuntimeMetricsAutoConfiguration.class);
32+
33+
@EventListener
34+
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");
42+
ConfigurableApplicationContext applicationContext = event.getApplicationContext();
43+
OpenTelemetry openTelemetry = applicationContext.getBean(OpenTelemetry.class);
44+
ConfigProperties configProperties = applicationContext.getBean(ConfigProperties.class);
45+
RuntimeMetrics.builder(openTelemetry)
46+
.startFromInstrumentationConfig(new ConfigPropertiesBridge(configProperties));
47+
}
48+
}

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/ConfigPropertiesBridge.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,13 @@
1313
import java.util.Map;
1414
import javax.annotation.Nullable;
1515

16-
final class ConfigPropertiesBridge implements InstrumentationConfig {
16+
/**
17+
* Support for {@link ConfigProperties} in {@link InstrumentationConfig}.
18+
*
19+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
20+
* at any time.
21+
*/
22+
public final class ConfigPropertiesBridge implements InstrumentationConfig {
1723

1824
private final ConfigProperties configProperties;
1925

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.r
1010
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.web.SpringWebInstrumentationAutoConfiguration,\
1111
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webflux.SpringWebfluxInstrumentationAutoConfiguration,\
1212
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webmvc.SpringWebMvc5InstrumentationAutoConfiguration,\
13-
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling.SpringSchedulingInstrumentationAutoConfiguration
13+
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling.SpringSchedulingInstrumentationAutoConfiguration,\
14+
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.Java17RuntimeMetricsAutoConfiguration
1415

1516
org.springframework.context.ApplicationListener=\
1617
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
Original file line numberDiff line numberDiff line change
@@ -11,3 +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

smoke-tests-otel-starter/spring-boot-common/src/main/java/io/opentelemetry/spring/smoketest/AbstractOtelSpringStarterSmokeTest.java

+5
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,11 @@ void shouldSendTelemetry() {
209209
OtelSpringStarterSmokeTestController.METER_SCOPE_NAME,
210210
OtelSpringStarterSmokeTestController.TEST_HISTOGRAM,
211211
AbstractIterableAssert::isNotEmpty);
212+
// runtime metrics
213+
testing.waitAndAssertMetrics(
214+
"io.opentelemetry.runtime-telemetry-java8",
215+
"jvm.thread.count",
216+
AbstractIterableAssert::isNotEmpty);
212217

213218
// Log
214219
List<LogRecordData> exportedLogRecords = testing.getExportedLogRecords();

smoke-tests-otel-starter/spring-boot-common/src/main/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTestController.java

-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
public class OtelSpringStarterSmokeTestController {
1616

1717
public static final String PING = "/ping";
18-
public static final String REST_CLIENT = "/rest-client";
19-
public static final String REST_TEMPLATE = "/rest-template";
2018
public static final String TEST_HISTOGRAM = "histogram-test-otel-spring-starter";
2119
public static final String METER_SCOPE_NAME = "scope";
2220
private final LongHistogram histogram;

0 commit comments

Comments
 (0)