Spring boot runtime metrics native reduced (#13173)
Co-authored-by: Jean Bisutti <jean.bisutti@gmail.com>
This commit is contained in:
parent
0e2cc4f81e
commit
9101f0300f
|
@ -1,2 +1,4 @@
|
|||
Args=\
|
||||
--initialize-at-build-time=io.opentelemetry.instrumentation.api.internal.cache.concurrentlinkedhashmap.ConcurrentLinkedHashMap
|
||||
--initialize-at-build-time=io.opentelemetry.instrumentation.api.internal.cache.concurrentlinkedhashmap.ConcurrentLinkedHashMap \
|
||||
--initialize-at-build-time=io.opentelemetry.instrumentation.api.internal.cache.MapBackedCache \
|
||||
--initialize-at-build-time=io.opentelemetry.api.internal.InternalAttributeKeyImpl
|
||||
|
|
|
@ -7,12 +7,11 @@ package io.opentelemetry.instrumentation.javaagent.runtimemetrics.java17;
|
|||
|
||||
import com.google.auto.service.AutoService;
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java17.RuntimeMetrics;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java17.RuntimeMetricsBuilder;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.RuntimeMetricsConfigUtil;
|
||||
import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig;
|
||||
import io.opentelemetry.javaagent.extension.AgentListener;
|
||||
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
|
||||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
|
||||
|
||||
/** An {@link AgentListener} that enables runtime metrics during agent startup. */
|
||||
@AutoService(AgentListener.class)
|
||||
|
@ -20,34 +19,13 @@ public class Java17RuntimeMetricsInstaller implements AgentListener {
|
|||
|
||||
@Override
|
||||
public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) {
|
||||
ConfigProperties config = AgentListener.resolveConfigProperties(autoConfiguredSdk);
|
||||
|
||||
OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
|
||||
RuntimeMetricsBuilder builder = null;
|
||||
/*
|
||||
By default don't use any JFR metrics. May change this once semantic conventions are updated.
|
||||
If enabled, default to only the metrics not already covered by runtime-telemetry-java8
|
||||
*/
|
||||
boolean defaultEnabled = config.getBoolean("otel.instrumentation.common.default-enabled", true);
|
||||
if (config.getBoolean("otel.instrumentation.runtime-telemetry-java17.enable-all", false)) {
|
||||
builder = RuntimeMetrics.builder(openTelemetry).enableAllFeatures();
|
||||
} else if (config.getBoolean("otel.instrumentation.runtime-telemetry-java17.enabled", false)) {
|
||||
builder = RuntimeMetrics.builder(openTelemetry);
|
||||
} else if (config.getBoolean(
|
||||
"otel.instrumentation.runtime-telemetry.enabled", defaultEnabled)) {
|
||||
// This only uses metrics gathered by JMX
|
||||
builder = RuntimeMetrics.builder(openTelemetry).disableAllFeatures();
|
||||
}
|
||||
|
||||
if (builder != null) {
|
||||
if (config.getBoolean(
|
||||
"otel.instrumentation.runtime-telemetry.emit-experimental-telemetry", false)) {
|
||||
builder.enableExperimentalJmxTelemetry();
|
||||
}
|
||||
|
||||
RuntimeMetrics finalJfrTelemetry = builder.build();
|
||||
Thread cleanupTelemetry = new Thread(() -> finalJfrTelemetry.close());
|
||||
Runtime.getRuntime().addShutdownHook(cleanupTelemetry);
|
||||
RuntimeMetrics runtimeMetrics =
|
||||
RuntimeMetricsConfigUtil.configure(
|
||||
RuntimeMetrics.builder(GlobalOpenTelemetry.get()), AgentInstrumentationConfig.get());
|
||||
if (runtimeMetrics != null) {
|
||||
Runtime.getRuntime()
|
||||
.addShutdownHook(
|
||||
new Thread(runtimeMetrics::close, "OpenTelemetry RuntimeMetricsShutdownHook"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import jdk.jfr.FlightRecorder;
|
|||
import jdk.jfr.consumer.RecordingStream;
|
||||
|
||||
/** The entry point class for runtime metrics support using JFR and JMX. */
|
||||
public final class RuntimeMetrics implements Closeable {
|
||||
public final class RuntimeMetrics implements AutoCloseable {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(RuntimeMetrics.class.getName());
|
||||
|
||||
|
@ -101,7 +101,7 @@ public final class RuntimeMetrics implements Closeable {
|
|||
recordingStream.onEvent(handler.getEventName(), handler);
|
||||
});
|
||||
recordingStream.onMetadata(event -> startUpLatch.countDown());
|
||||
Thread daemonRunner = new Thread(() -> recordingStream.start());
|
||||
Thread daemonRunner = new Thread(recordingStream::start, "OpenTelemetry JFR-Metrics-Runner");
|
||||
daemonRunner.setDaemon(true);
|
||||
daemonRunner.start();
|
||||
}
|
||||
|
@ -138,7 +138,8 @@ public final class RuntimeMetrics implements Closeable {
|
|||
private static boolean isJfrAvailable() {
|
||||
try {
|
||||
Class.forName("jdk.jfr.FlightRecorder");
|
||||
} catch (ClassNotFoundException e) {
|
||||
// UnsatisfiedLinkError or ClassNotFoundException
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,17 +7,8 @@ package io.opentelemetry.instrumentation.runtimemetrics.java17;
|
|||
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.Classes;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.Cpu;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.GarbageCollector;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.MemoryPools;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.Threads;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalBufferPools;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalCpu;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalMemoryPools;
|
||||
import java.util.ArrayList;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsFactory;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
|
@ -83,7 +74,7 @@ public final class RuntimeMetricsBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/** Disable telemetry collection associated with the {@link JfrFeature}. */
|
||||
/** Enable experimental JMX telemetry collection. */
|
||||
@CanIgnoreReturnValue
|
||||
public RuntimeMetricsBuilder enableExperimentalJmxTelemetry() {
|
||||
enableExperimentalJmxTelemetry = true;
|
||||
|
@ -92,35 +83,15 @@ public final class RuntimeMetricsBuilder {
|
|||
|
||||
/** Build and start an {@link RuntimeMetrics} with the config from this builder. */
|
||||
public RuntimeMetrics build() {
|
||||
List<AutoCloseable> observables = buildObservables();
|
||||
List<AutoCloseable> observables =
|
||||
disableJmx
|
||||
? List.of()
|
||||
: JmxRuntimeMetricsFactory.buildObservables(
|
||||
openTelemetry, enableExperimentalJmxTelemetry);
|
||||
RuntimeMetrics.JfrRuntimeMetrics jfrRuntimeMetrics = buildJfrMetrics();
|
||||
return new RuntimeMetrics(openTelemetry, observables, jfrRuntimeMetrics);
|
||||
}
|
||||
|
||||
@SuppressWarnings("CatchingUnchecked")
|
||||
private List<AutoCloseable> buildObservables() {
|
||||
if (disableJmx) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
// 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(MemoryPools.registerObservers(openTelemetry));
|
||||
observables.addAll(Threads.registerObservers(openTelemetry));
|
||||
if (enableExperimentalJmxTelemetry) {
|
||||
observables.addAll(ExperimentalBufferPools.registerObservers(openTelemetry));
|
||||
observables.addAll(ExperimentalCpu.registerObservers(openTelemetry));
|
||||
observables.addAll(ExperimentalMemoryPools.registerObservers(openTelemetry));
|
||||
}
|
||||
return observables;
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Error building RuntimeMetrics", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private RuntimeMetrics.JfrRuntimeMetrics buildJfrMetrics() {
|
||||
if (enabledFeatureMap.values().stream().noneMatch(isEnabled -> isEnabled)) {
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.runtimemetrics.java17.internal;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java17.RuntimeMetrics;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java17.RuntimeMetricsBuilder;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
|
||||
* any time.
|
||||
*/
|
||||
public final class RuntimeMetricsConfigUtil {
|
||||
private RuntimeMetricsConfigUtil() {}
|
||||
|
||||
@Nullable
|
||||
public static RuntimeMetrics configure(
|
||||
RuntimeMetricsBuilder builder, InstrumentationConfig config) {
|
||||
/*
|
||||
By default, don't use any JFR metrics. May change this once semantic conventions are updated.
|
||||
If enabled, default to only the metrics not already covered by runtime-telemetry-java8
|
||||
*/
|
||||
boolean defaultEnabled = config.getBoolean("otel.instrumentation.common.default-enabled", true);
|
||||
if (config.getBoolean("otel.instrumentation.runtime-telemetry-java17.enable-all", false)) {
|
||||
builder.enableAllFeatures();
|
||||
} else if (config.getBoolean("otel.instrumentation.runtime-telemetry-java17.enabled", false)) {
|
||||
// default configuration
|
||||
} else if (config.getBoolean(
|
||||
"otel.instrumentation.runtime-telemetry.enabled", defaultEnabled)) {
|
||||
// This only uses metrics gathered by JMX
|
||||
builder.disableAllFeatures();
|
||||
} else {
|
||||
// nothing is enabled
|
||||
return null;
|
||||
}
|
||||
|
||||
if (config.getBoolean(
|
||||
"otel.instrumentation.runtime-telemetry.emit-experimental-telemetry", false)) {
|
||||
builder.enableExperimentalJmxTelemetry();
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
}
|
|
@ -7,21 +7,11 @@ 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.Classes;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.Cpu;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.GarbageCollector;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.MemoryPools;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.Threads;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalBufferPools;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalCpu;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.ExperimentalMemoryPools;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsUtil;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.RuntimeMetrics;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.RuntimeMetricsConfigUtil;
|
||||
import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig;
|
||||
import io.opentelemetry.javaagent.extension.AgentListener;
|
||||
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
|
||||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** An {@link AgentListener} that enables runtime metrics during agent startup. */
|
||||
@AutoService(AgentListener.class)
|
||||
|
@ -29,30 +19,13 @@ public class Java8RuntimeMetricsInstaller implements AgentListener {
|
|||
|
||||
@Override
|
||||
public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) {
|
||||
ConfigProperties config = AgentListener.resolveConfigProperties(autoConfiguredSdk);
|
||||
|
||||
boolean defaultEnabled = config.getBoolean("otel.instrumentation.common.default-enabled", true);
|
||||
if (!config.getBoolean("otel.instrumentation.runtime-telemetry.enabled", defaultEnabled)
|
||||
|| Double.parseDouble(System.getProperty("java.specification.version")) >= 17) {
|
||||
return;
|
||||
RuntimeMetrics runtimeMetrics =
|
||||
RuntimeMetricsConfigUtil.configure(
|
||||
RuntimeMetrics.builder(GlobalOpenTelemetry.get()), AgentInstrumentationConfig.get());
|
||||
if (runtimeMetrics != null) {
|
||||
Runtime.getRuntime()
|
||||
.addShutdownHook(
|
||||
new Thread(runtimeMetrics::close, "OpenTelemetry RuntimeMetricsShutdownHook"));
|
||||
}
|
||||
|
||||
OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
|
||||
List<AutoCloseable> observables = new ArrayList<>();
|
||||
observables.addAll(Classes.registerObservers(openTelemetry));
|
||||
observables.addAll(Cpu.registerObservers(openTelemetry));
|
||||
observables.addAll(GarbageCollector.registerObservers(openTelemetry));
|
||||
observables.addAll(MemoryPools.registerObservers(openTelemetry));
|
||||
observables.addAll(Threads.registerObservers(openTelemetry));
|
||||
|
||||
if (config.getBoolean(
|
||||
"otel.instrumentation.runtime-telemetry.emit-experimental-telemetry", false)) {
|
||||
observables.addAll(ExperimentalBufferPools.registerObservers(openTelemetry));
|
||||
observables.addAll(ExperimentalCpu.registerObservers(openTelemetry));
|
||||
observables.addAll(ExperimentalMemoryPools.registerObservers(openTelemetry));
|
||||
}
|
||||
|
||||
Thread cleanupTelemetry = new Thread(() -> JmxRuntimeMetricsUtil.closeObservers(observables));
|
||||
Runtime.getRuntime().addShutdownHook(cleanupTelemetry);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.runtimemetrics.java8;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsUtil;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/** The entry point class for runtime metrics support using JMX. */
|
||||
public final class RuntimeMetrics implements AutoCloseable {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(RuntimeMetrics.class.getName());
|
||||
|
||||
private final AtomicBoolean isClosed = new AtomicBoolean();
|
||||
private final List<AutoCloseable> observables;
|
||||
|
||||
RuntimeMetrics(List<AutoCloseable> observables) {
|
||||
this.observables = Collections.unmodifiableList(observables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and start {@link RuntimeMetrics}.
|
||||
*
|
||||
* <p>Listens for select JMX beans, extracts data, and records to various metrics. Recording will
|
||||
* continue until {@link #close()} is called.
|
||||
*
|
||||
* @param openTelemetry the {@link OpenTelemetry} instance used to record telemetry
|
||||
*/
|
||||
public static RuntimeMetrics create(OpenTelemetry openTelemetry) {
|
||||
return new RuntimeMetricsBuilder(openTelemetry).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a builder for configuring {@link RuntimeMetrics}.
|
||||
*
|
||||
* @param openTelemetry the {@link OpenTelemetry} instance used to record telemetry
|
||||
*/
|
||||
public static RuntimeMetricsBuilder builder(OpenTelemetry openTelemetry) {
|
||||
return new RuntimeMetricsBuilder(openTelemetry);
|
||||
}
|
||||
|
||||
/** Stop recording JMX metrics. */
|
||||
@Override
|
||||
public void close() {
|
||||
if (!isClosed.compareAndSet(false, true)) {
|
||||
logger.log(Level.WARNING, "RuntimeMetrics is already closed");
|
||||
return;
|
||||
}
|
||||
|
||||
JmxRuntimeMetricsUtil.closeObservers(observables);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.runtimemetrics.java8;
|
||||
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsFactory;
|
||||
import java.util.List;
|
||||
|
||||
/** Builder for {@link RuntimeMetrics}. */
|
||||
public final class RuntimeMetricsBuilder {
|
||||
|
||||
private final OpenTelemetry openTelemetry;
|
||||
|
||||
private boolean enableExperimentalJmxTelemetry = false;
|
||||
|
||||
RuntimeMetricsBuilder(OpenTelemetry openTelemetry) {
|
||||
this.openTelemetry = openTelemetry;
|
||||
}
|
||||
|
||||
/** Enable all JMX telemetry collection. */
|
||||
@CanIgnoreReturnValue
|
||||
public RuntimeMetricsBuilder enableExperimentalJmxTelemetry() {
|
||||
enableExperimentalJmxTelemetry = 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);
|
||||
return new RuntimeMetrics(observables);
|
||||
}
|
||||
}
|
|
@ -57,7 +57,7 @@ public final class Threads {
|
|||
|
||||
/** Register observers for java runtime class metrics. */
|
||||
public static List<AutoCloseable> registerObservers(OpenTelemetry openTelemetry) {
|
||||
return INSTANCE.registerObservers(openTelemetry, !isJava9OrNewer());
|
||||
return INSTANCE.registerObservers(openTelemetry, useThreads());
|
||||
}
|
||||
|
||||
private List<AutoCloseable> registerObservers(OpenTelemetry openTelemetry, boolean useThread) {
|
||||
|
@ -116,6 +116,13 @@ public final class Threads {
|
|||
return THREAD_INFO_IS_DAEMON != null;
|
||||
}
|
||||
|
||||
private static boolean useThreads() {
|
||||
// GraalVM native image does not support ThreadMXBean yet
|
||||
// see https://github.com/oracle/graal/issues/6101
|
||||
boolean isNativeExecution = System.getProperty("org.graalvm.nativeimage.imagecode") != null;
|
||||
return !isJava9OrNewer() || isNativeExecution;
|
||||
}
|
||||
|
||||
private static Consumer<ObservableLongMeasurement> java8Callback(ThreadMXBean threadBean) {
|
||||
return measurement -> {
|
||||
int daemonThreadCount = threadBean.getDaemonThreadCount();
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.runtimemetrics.java8.internal;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.Classes;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.Cpu;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.GarbageCollector;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.MemoryPools;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.Threads;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
|
||||
* any time.
|
||||
*/
|
||||
public class JmxRuntimeMetricsFactory {
|
||||
@SuppressWarnings("CatchingUnchecked")
|
||||
public static List<AutoCloseable> buildObservables(
|
||||
OpenTelemetry openTelemetry, boolean enableExperimentalJmxTelemetry) {
|
||||
// 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(MemoryPools.registerObservers(openTelemetry));
|
||||
observables.addAll(Threads.registerObservers(openTelemetry));
|
||||
if (enableExperimentalJmxTelemetry) {
|
||||
observables.addAll(ExperimentalBufferPools.registerObservers(openTelemetry));
|
||||
observables.addAll(ExperimentalCpu.registerObservers(openTelemetry));
|
||||
observables.addAll(ExperimentalMemoryPools.registerObservers(openTelemetry));
|
||||
}
|
||||
return observables;
|
||||
}
|
||||
|
||||
private JmxRuntimeMetricsFactory() {}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.runtimemetrics.java8.internal;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.RuntimeMetrics;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.RuntimeMetricsBuilder;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
|
||||
* any time.
|
||||
*/
|
||||
public final class RuntimeMetricsConfigUtil {
|
||||
private RuntimeMetricsConfigUtil() {}
|
||||
|
||||
@Nullable
|
||||
public static RuntimeMetrics configure(
|
||||
RuntimeMetricsBuilder builder, InstrumentationConfig config) {
|
||||
boolean defaultEnabled = config.getBoolean("otel.instrumentation.common.default-enabled", true);
|
||||
if (!config.getBoolean("otel.instrumentation.runtime-telemetry.enabled", defaultEnabled)) {
|
||||
// nothing is enabled
|
||||
return null;
|
||||
}
|
||||
|
||||
if (config.getBoolean(
|
||||
"otel.instrumentation.runtime-telemetry.emit-experimental-telemetry", false)) {
|
||||
builder.enableExperimentalJmxTelemetry();
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
[
|
||||
{
|
||||
"name": "com.sun.management.OperatingSystemMXBean",
|
||||
"allPublicMethods": true
|
||||
},
|
||||
{
|
||||
"name": "com.ibm.lang.management.OperatingSystemMXBean",
|
||||
"allPublicMethods": true
|
||||
}
|
||||
]
|
|
@ -53,6 +53,8 @@ dependencies {
|
|||
implementation(project(":instrumentation:logback:logback-mdc-1.0:library"))
|
||||
compileOnly("ch.qos.logback:logback-classic:1.0.0")
|
||||
implementation(project(":instrumentation:jdbc:library"))
|
||||
implementation(project(":instrumentation:runtime-telemetry:runtime-telemetry-java8:library"))
|
||||
implementation(project(":instrumentation:runtime-telemetry:runtime-telemetry-java17:library"))
|
||||
|
||||
library("org.springframework.kafka:spring-kafka:2.9.0")
|
||||
library("org.springframework.boot:spring-boot-starter-actuator:$springBootVersion")
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java17.RuntimeMetrics;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.RuntimeMetricsConfigUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Configures runtime metrics collection for Java 17+.
|
||||
*
|
||||
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
|
||||
* at any time.
|
||||
*/
|
||||
public class Java17RuntimeMetricsProvider implements RuntimeMetricsProvider {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Java17RuntimeMetricsProvider.class);
|
||||
|
||||
@Override
|
||||
public int minJavaVersion() {
|
||||
return 17;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoCloseable start(OpenTelemetry openTelemetry, InstrumentationConfig config) {
|
||||
logger.debug("Use runtime metrics instrumentation for Java 17+");
|
||||
return RuntimeMetricsConfigUtil.configure(RuntimeMetrics.builder(openTelemetry), config);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.RuntimeMetrics;
|
||||
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.RuntimeMetricsConfigUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Configures runtime metrics collection for Java 8.
|
||||
*
|
||||
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
|
||||
* at any time.
|
||||
*/
|
||||
public class Java8RuntimeMetricsProvider implements RuntimeMetricsProvider {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Java8RuntimeMetricsProvider.class);
|
||||
|
||||
@Override
|
||||
public int minJavaVersion() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoCloseable start(OpenTelemetry openTelemetry, InstrumentationConfig config) {
|
||||
logger.debug("Use runtime metrics instrumentation for Java 8");
|
||||
return RuntimeMetricsConfigUtil.configure(RuntimeMetrics.builder(openTelemetry), config);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation;
|
||||
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.ConfigPropertiesBridge;
|
||||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.PreDestroy;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.EventListener;
|
||||
|
||||
/**
|
||||
* Configures runtime metrics collection.
|
||||
*
|
||||
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
|
||||
* at any time.
|
||||
*/
|
||||
@ConditionalOnEnabledInstrumentation(module = "runtime-telemetry")
|
||||
@Configuration
|
||||
public class RuntimeMetricsAutoConfiguration {
|
||||
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(RuntimeMetricsAutoConfiguration.class);
|
||||
|
||||
private AutoCloseable closeable;
|
||||
|
||||
@PreDestroy
|
||||
public void stopMetrics() throws Exception {
|
||||
if (closeable != null) {
|
||||
closeable.close();
|
||||
}
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void handleApplicationReadyEvent(ApplicationReadyEvent event) {
|
||||
ConfigurableApplicationContext applicationContext = event.getApplicationContext();
|
||||
OpenTelemetry openTelemetry = applicationContext.getBean(OpenTelemetry.class);
|
||||
ConfigPropertiesBridge config =
|
||||
new ConfigPropertiesBridge(applicationContext.getBean(ConfigProperties.class));
|
||||
|
||||
double version =
|
||||
Math.max(8, Double.parseDouble(System.getProperty("java.specification.version")));
|
||||
Optional<RuntimeMetricsProvider> metricsProvider =
|
||||
applicationContext.getBeanProvider(RuntimeMetricsProvider.class).stream()
|
||||
.sorted(Comparator.comparing(RuntimeMetricsProvider::minJavaVersion).reversed())
|
||||
.filter(provider -> provider.minJavaVersion() <= version)
|
||||
.findFirst();
|
||||
|
||||
if (metricsProvider.isPresent()) {
|
||||
this.closeable = metricsProvider.get().start(openTelemetry, config);
|
||||
} else {
|
||||
logger.debug("No runtime metrics instrumentation available for Java {}", version);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Configures runtime metrics collection.
|
||||
*
|
||||
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
|
||||
* at any time.
|
||||
*/
|
||||
public interface RuntimeMetricsProvider {
|
||||
int minJavaVersion();
|
||||
|
||||
@Nullable
|
||||
AutoCloseable start(OpenTelemetry openTelemetry, InstrumentationConfig config);
|
||||
}
|
|
@ -13,7 +13,13 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
final class ConfigPropertiesBridge implements InstrumentationConfig {
|
||||
/**
|
||||
* Support for {@link ConfigProperties} in {@link InstrumentationConfig}.
|
||||
*
|
||||
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
|
||||
* at any time.
|
||||
*/
|
||||
public final class ConfigPropertiesBridge implements InstrumentationConfig {
|
||||
|
||||
private final ConfigProperties configProperties;
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics;
|
||||
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
|
||||
/**
|
||||
* Configures runtime metrics collection for Java 17+.
|
||||
*
|
||||
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
|
||||
* at any time.
|
||||
*/
|
||||
public class RuntimeMetricsBeanRegistrationExcludeFilter implements BeanRegistrationExcludeFilter {
|
||||
@Override
|
||||
public boolean isExcludedFromAotProcessing(RegisteredBean registeredBean) {
|
||||
// The JFR-based runtime metric code is excluded from the Spring AOT processing step.
|
||||
// That way, this code is not included in a Spring native image application.
|
||||
|
||||
return Java17RuntimeMetricsProvider.class.getName().equals(registeredBean.getBeanName());
|
||||
}
|
||||
}
|
|
@ -476,6 +476,30 @@
|
|||
"description": "Enables the DB statement sanitization.",
|
||||
"defaultValue": true
|
||||
},
|
||||
{
|
||||
"name": "otel.instrumentation.runtime-telemetry.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Enable runtime telemetry metrics.",
|
||||
"defaultValue": true
|
||||
},
|
||||
{
|
||||
"name": "otel.instrumentation.runtime-telemetry.emit-experimental-telemetry",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Enable the capture of experimental metrics.",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"name": "otel.instrumentation.runtime-telemetry-java17.enable-all",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Enable the capture of all JFR based metrics.",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"name": "otel.instrumentation.runtime-telemetry-java17.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Enable the capture of JFR based metrics.",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"name": "otel.instrumentation.spring-web.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
|
|
|
@ -10,7 +10,10 @@ io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.r
|
|||
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.web.SpringWebInstrumentationAutoConfiguration,\
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webflux.SpringWebfluxInstrumentationAutoConfiguration,\
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webmvc.SpringWebMvc5InstrumentationAutoConfiguration,\
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling.SpringSchedulingInstrumentationAutoConfiguration
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling.SpringSchedulingInstrumentationAutoConfiguration,\
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.RuntimeMetricsAutoConfiguration,\
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.Java8RuntimeMetricsProvider,\
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.Java17RuntimeMetricsProvider
|
||||
|
||||
org.springframework.context.ApplicationListener=\
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.logging.LogbackAppenderApplicationListener
|
||||
|
|
|
@ -1,2 +1,5 @@
|
|||
org.springframework.aot.hint.RuntimeHintsRegistrar=\
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.annotations.OpenTelemetryAnnotationsRuntimeHints
|
||||
|
||||
org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter=\
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.RuntimeMetricsBeanRegistrationExcludeFilter
|
||||
|
|
|
@ -11,3 +11,6 @@ io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.w
|
|||
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.web.RestClientInstrumentationAutoConfiguration
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webmvc.SpringWebMvc6InstrumentationAutoConfiguration
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling.SpringSchedulingInstrumentationAutoConfiguration
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.RuntimeMetricsAutoConfiguration
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.Java8RuntimeMetricsProvider
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.Java17RuntimeMetricsProvider
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.springframework.boot.test.context.SpringBootTest;
|
|||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
properties = {
|
||||
// The headers are simply set here to make sure that headers can be parsed
|
||||
"otel.exporter.otlp.headers.c=3"
|
||||
"otel.exporter.otlp.headers.c=3",
|
||||
"otel.instrumentation.runtime-telemetry.emit-experimental-telemetry=true",
|
||||
})
|
||||
class OtelSpringStarterSmokeTest extends AbstractOtelSpringStarterSmokeTest {}
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
package io.opentelemetry.spring.smoketest;
|
||||
|
||||
import java.util.List;
|
||||
import org.assertj.core.api.AbstractIterableAssert;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest(
|
||||
|
@ -16,6 +18,36 @@ import org.springframework.boot.test.context.SpringBootTest;
|
|||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
properties = {
|
||||
// The headers are simply set here to make sure that headers can be parsed
|
||||
"otel.exporter.otlp.headers.c=3"
|
||||
"otel.exporter.otlp.headers.c=3",
|
||||
"otel.instrumentation.runtime-telemetry.emit-experimental-telemetry=true",
|
||||
"otel.instrumentation.runtime-telemetry-java17.enable-all=true",
|
||||
})
|
||||
class OtelSpringStarterSmokeTest extends AbstractOtelSpringStarterSmokeTest {}
|
||||
class OtelSpringStarterSmokeTest extends AbstractOtelSpringStarterSmokeTest {
|
||||
|
||||
@Override
|
||||
protected void assertAdditionalMetrics() {
|
||||
if (System.getProperty("org.graalvm.nativeimage.imagecode") != null) {
|
||||
// GraalVM native image does not support JFR
|
||||
return;
|
||||
}
|
||||
|
||||
// JFR based metrics
|
||||
for (String metric :
|
||||
List.of(
|
||||
"jvm.cpu.limit",
|
||||
"jvm.buffer.count",
|
||||
"jvm.class.count",
|
||||
"jvm.cpu.context_switch",
|
||||
"jvm.cpu.longlock",
|
||||
"jvm.system.cpu.utilization",
|
||||
"jvm.gc.duration",
|
||||
"jvm.memory.init",
|
||||
"jvm.memory.used",
|
||||
"jvm.memory.allocation",
|
||||
"jvm.network.io",
|
||||
"jvm.thread.count")) {
|
||||
testing.waitAndAssertMetrics(
|
||||
"io.opentelemetry.runtime-telemetry-java17", metric, AbstractIterableAssert::isNotEmpty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ import io.opentelemetry.semconv.incubating.CodeIncubatingAttributes;
|
|||
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
|
||||
import io.opentelemetry.semconv.incubating.ServiceIncubatingAttributes;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.assertj.core.api.AbstractCharSequenceAssert;
|
||||
|
@ -210,10 +212,33 @@ class AbstractOtelSpringStarterSmokeTest extends AbstractSpringStarterSmokeTest
|
|||
OtelSpringStarterSmokeTestController.TEST_HISTOGRAM,
|
||||
AbstractIterableAssert::isNotEmpty);
|
||||
|
||||
// JMX based metrics - test one per JMX bean
|
||||
List<String> jmxMetrics =
|
||||
new ArrayList<>(
|
||||
Arrays.asList(
|
||||
"jvm.thread.count",
|
||||
"jvm.memory.used",
|
||||
"jvm.system.cpu.load_1m",
|
||||
"jvm.memory.init"));
|
||||
|
||||
boolean noNative = System.getProperty("org.graalvm.nativeimage.imagecode") == null;
|
||||
if (noNative) {
|
||||
// GraalVM native image does not support buffer pools - have to investigate why
|
||||
jmxMetrics.add("jvm.buffer.memory.usage");
|
||||
}
|
||||
jmxMetrics.forEach(
|
||||
metricName ->
|
||||
testing.waitAndAssertMetrics(
|
||||
"io.opentelemetry.runtime-telemetry-java8",
|
||||
metricName,
|
||||
AbstractIterableAssert::isNotEmpty));
|
||||
|
||||
assertAdditionalMetrics();
|
||||
|
||||
// Log
|
||||
List<LogRecordData> exportedLogRecords = testing.getExportedLogRecords();
|
||||
assertThat(exportedLogRecords).as("No log record exported.").isNotEmpty();
|
||||
if (System.getProperty("org.graalvm.nativeimage.imagecode") == null) {
|
||||
if (noNative) {
|
||||
// log records differ in native image mode due to different startup timing
|
||||
LogRecordData firstLog = exportedLogRecords.get(0);
|
||||
assertThat(firstLog.getBodyValue().asString())
|
||||
|
@ -228,6 +253,8 @@ class AbstractOtelSpringStarterSmokeTest extends AbstractSpringStarterSmokeTest
|
|||
}
|
||||
}
|
||||
|
||||
protected void assertAdditionalMetrics() {}
|
||||
|
||||
@Test
|
||||
void databaseQuery() {
|
||||
testing.clearAllExportedData();
|
||||
|
|
|
@ -15,8 +15,6 @@ import org.springframework.web.bind.annotation.RestController;
|
|||
public class OtelSpringStarterSmokeTestController {
|
||||
|
||||
public static final String PING = "/ping";
|
||||
public static final String REST_CLIENT = "/rest-client";
|
||||
public static final String REST_TEMPLATE = "/rest-template";
|
||||
public static final String TEST_HISTOGRAM = "histogram-test-otel-spring-starter";
|
||||
public static final String METER_SCOPE_NAME = "scope";
|
||||
private final LongHistogram histogram;
|
||||
|
|
Loading…
Reference in New Issue