mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-17 12:09:16 +00:00
Configure OpenTelemetry semantic conventions
Adds a management.observations.conventions property to control which set of conventions are auto-configured across the application. Configures WebMvc and WebFlux ServerRequestObservationConvention beans to switch to OpenTelemetry conventions when configured. Configures JVM and system meter binders using their builder APIs to apply OpenTelemetry conventions while still allowing individual meter conventions to be overridden via beans. Signed-off-by: Tommy Ludwig <8924140+shakuzen@users.noreply.github.com> See gh-49241
This commit is contained in:
committed by
Andy Wilkinson
parent
254a2e37ba
commit
7d43f31732
+36
-14
@@ -34,6 +34,7 @@ import io.micrometer.core.instrument.binder.jvm.convention.JvmClassUnloadedMeter
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.JvmMemoryCommittedMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.JvmMemoryMaxMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.JvmMemoryMeterConventions;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.JvmMemoryUsedAfterLastGcMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.JvmMemoryUsedMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.JvmThreadCountMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.JvmThreadMeterConventions;
|
||||
@@ -49,9 +50,12 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.boot.micrometer.metrics.autoconfigure.CompositeMeterRegistryAutoConfiguration;
|
||||
import org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAutoConfiguration;
|
||||
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties;
|
||||
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties.ConventionsVariant;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportRuntimeHints;
|
||||
@@ -67,6 +71,7 @@ import org.springframework.util.ClassUtils;
|
||||
@AutoConfiguration(after = { MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class })
|
||||
@ConditionalOnClass(MeterRegistry.class)
|
||||
@ConditionalOnBean(MeterRegistry.class)
|
||||
@EnableConfigurationProperties(ObservationProperties.class)
|
||||
public final class JvmMetricsAutoConfiguration {
|
||||
|
||||
private static final String VIRTUAL_THREAD_METRICS_CLASS = "io.micrometer.java21.instrument.binder.jdk.VirtualThreadMetrics";
|
||||
@@ -86,26 +91,29 @@ public final class JvmMetricsAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@SuppressWarnings("deprecation")
|
||||
JvmMemoryMetrics jvmMemoryMetrics(ObjectProvider<JvmMemoryMeterConventions> jvmMemoryMeterConventions,
|
||||
JvmMemoryMetrics jvmMemoryMetrics(ObservationProperties observationProperties,
|
||||
ObjectProvider<JvmMemoryMeterConventions> jvmMemoryMeterConventions,
|
||||
ObjectProvider<JvmMemoryUsedMeterConvention> jvmMemoryUsedMeterConvention,
|
||||
ObjectProvider<JvmMemoryCommittedMeterConvention> jvmMemoryCommittedMeterConvention,
|
||||
ObjectProvider<JvmMemoryMaxMeterConvention> jvmMemoryMaxMeterConvention) {
|
||||
ObjectProvider<JvmMemoryMaxMeterConvention> jvmMemoryMaxMeterConvention,
|
||||
ObjectProvider<JvmMemoryUsedAfterLastGcMeterConvention> jvmMemoryUsedAfterLastGcMeterConvention) {
|
||||
JvmMemoryMetricsFactory factory = new JvmMemoryMetricsFactory(jvmMemoryUsedMeterConvention.getIfAvailable(),
|
||||
jvmMemoryCommittedMeterConvention.getIfAvailable(), jvmMemoryMaxMeterConvention.getIfAvailable());
|
||||
jvmMemoryCommittedMeterConvention.getIfAvailable(), jvmMemoryMaxMeterConvention.getIfAvailable(),
|
||||
jvmMemoryUsedAfterLastGcMeterConvention.getIfAvailable());
|
||||
JvmMemoryMeterConventions deprecatedConventions = jvmMemoryMeterConventions.getIfAvailable();
|
||||
if (deprecatedConventions != null && factory.hasConvention()) {
|
||||
throw new IllegalStateException("Either %s or the interfaces that supersede it should be set"
|
||||
.formatted(JvmMemoryMeterConventions.class.getSimpleName()));
|
||||
}
|
||||
return (deprecatedConventions != null) ? new JvmMemoryMetrics(Collections.emptyList(), deprecatedConventions)
|
||||
: factory.create();
|
||||
|
||||
: factory.create(observationProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@SuppressWarnings("deprecation")
|
||||
JvmThreadMetrics jvmThreadMetrics(ObjectProvider<JvmThreadMeterConventions> jvmThreadMeterConventions,
|
||||
JvmThreadMetrics jvmThreadMetrics(ObservationProperties observationProperties,
|
||||
ObjectProvider<JvmThreadMeterConventions> jvmThreadMeterConventions,
|
||||
ObjectProvider<JvmThreadCountMeterConvention> jvmThreadCountMeterConvention) {
|
||||
JvmThreadMeterConventions deprecatedConventions = jvmThreadMeterConventions.getIfAvailable();
|
||||
JvmThreadCountMeterConvention convention = jvmThreadCountMeterConvention.getIfAvailable();
|
||||
@@ -117,14 +125,19 @@ public final class JvmMetricsAutoConfiguration {
|
||||
if (deprecatedConventions != null) {
|
||||
return new JvmThreadMetrics(Collections.emptyList(), deprecatedConventions);
|
||||
}
|
||||
return (convention != null) ? JvmThreadMetrics.builder().threadCountConvention(convention).build()
|
||||
: new JvmThreadMetrics();
|
||||
JvmThreadMetrics.Builder builder = JvmThreadMetrics.builder();
|
||||
if (observationProperties.getConventions() == ConventionsVariant.OPENTELEMETRY) {
|
||||
builder.openTelemetryConventions();
|
||||
}
|
||||
PropertyMapper map = PropertyMapper.get();
|
||||
map.from(convention).to(builder::threadCountConvention);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@SuppressWarnings("deprecation")
|
||||
ClassLoaderMetrics classLoaderMetrics(
|
||||
ClassLoaderMetrics classLoaderMetrics(ObservationProperties observationProperties,
|
||||
ObjectProvider<JvmClassLoadingMeterConventions> jvmClassLoadingMeterConventions,
|
||||
ObjectProvider<JvmClassCountMeterConvention> jvmClassCountMeterConvention,
|
||||
ObjectProvider<JvmClassLoadedMeterConvention> jvmClassLoadedMeterConvention,
|
||||
@@ -136,7 +149,8 @@ public final class JvmMetricsAutoConfiguration {
|
||||
throw new IllegalStateException("Either %s or the interfaces that supersede it should be set"
|
||||
.formatted(JvmClassLoadingMeterConventions.class.getSimpleName()));
|
||||
}
|
||||
return (deprecatedConventions != null) ? new ClassLoaderMetrics(deprecatedConventions) : factory.create();
|
||||
return (deprecatedConventions != null) ? new ClassLoaderMetrics(deprecatedConventions)
|
||||
: factory.create(observationProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -168,19 +182,24 @@ public final class JvmMetricsAutoConfiguration {
|
||||
|
||||
private record JvmMemoryMetricsFactory(@Nullable JvmMemoryUsedMeterConvention memoryUsedConvention,
|
||||
@Nullable JvmMemoryCommittedMeterConvention memoryCommittedConvention,
|
||||
@Nullable JvmMemoryMaxMeterConvention memoryMaxConvention) {
|
||||
@Nullable JvmMemoryMaxMeterConvention memoryMaxConvention,
|
||||
@Nullable JvmMemoryUsedAfterLastGcMeterConvention memoryUsedAfterLastGcConvention) {
|
||||
|
||||
boolean hasConvention() {
|
||||
return this.memoryUsedConvention != null || this.memoryCommittedConvention != null
|
||||
|| this.memoryMaxConvention != null;
|
||||
|| this.memoryMaxConvention != null || this.memoryUsedAfterLastGcConvention != null;
|
||||
}
|
||||
|
||||
JvmMemoryMetrics create() {
|
||||
JvmMemoryMetrics create(ObservationProperties observationProperties) {
|
||||
JvmMemoryMetrics.Builder builder = JvmMemoryMetrics.builder();
|
||||
if (observationProperties.getConventions() == ConventionsVariant.OPENTELEMETRY) {
|
||||
builder.openTelemetryConventions();
|
||||
}
|
||||
PropertyMapper map = PropertyMapper.get();
|
||||
map.from(this.memoryUsedConvention).to(builder::memoryUsedConvention);
|
||||
map.from(this.memoryCommittedConvention).to(builder::memoryCommittedConvention);
|
||||
map.from(this.memoryMaxConvention).to(builder::memoryMaxConvention);
|
||||
map.from(this.memoryUsedAfterLastGcConvention).to(builder::memoryUsedAfterLastGcConvention);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -195,8 +214,11 @@ public final class JvmMetricsAutoConfiguration {
|
||||
|| this.classUnloadedConvention != null;
|
||||
}
|
||||
|
||||
ClassLoaderMetrics create() {
|
||||
ClassLoaderMetrics create(ObservationProperties observationProperties) {
|
||||
ClassLoaderMetrics.Builder builder = ClassLoaderMetrics.builder();
|
||||
if (observationProperties.getConventions() == ConventionsVariant.OPENTELEMETRY) {
|
||||
builder.openTelemetryConventions();
|
||||
}
|
||||
PropertyMapper map = PropertyMapper.get();
|
||||
map.from(this.classCountConvention).to(builder::classCountConvention);
|
||||
map.from(this.classLoadedConvention).to(builder::classLoadedConvention);
|
||||
|
||||
+11
-5
@@ -43,6 +43,8 @@ import org.springframework.boot.micrometer.metrics.autoconfigure.CompositeMeterR
|
||||
import org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAutoConfiguration;
|
||||
import org.springframework.boot.micrometer.metrics.autoconfigure.MetricsProperties;
|
||||
import org.springframework.boot.micrometer.metrics.system.DiskSpaceMetricsBinder;
|
||||
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties;
|
||||
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties.ConventionsVariant;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
@@ -55,7 +57,7 @@ import org.springframework.context.annotation.Bean;
|
||||
@AutoConfiguration(after = { MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class })
|
||||
@ConditionalOnClass(MeterRegistry.class)
|
||||
@ConditionalOnBean(MeterRegistry.class)
|
||||
@EnableConfigurationProperties(MetricsProperties.class)
|
||||
@EnableConfigurationProperties({ MetricsProperties.class, ObservationProperties.class })
|
||||
public final class SystemMetricsAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@@ -67,7 +69,8 @@ public final class SystemMetricsAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@SuppressWarnings("deprecation")
|
||||
ProcessorMetrics processorMetrics(ObjectProvider<JvmCpuMeterConventions> jvmCpuMeterConventions,
|
||||
ProcessorMetrics processorMetrics(ObservationProperties observationProperties,
|
||||
ObjectProvider<JvmCpuMeterConventions> jvmCpuMeterConventions,
|
||||
ObjectProvider<JvmCpuTimeMeterConvention> jvmCpuTimeMeterConvention,
|
||||
ObjectProvider<JvmCpuCountMeterConvention> jvmCpuCountMeterConvention,
|
||||
ObjectProvider<JvmCpuLoadMeterConvention> jvmCpuLoadMeterConvention) {
|
||||
@@ -79,7 +82,7 @@ public final class SystemMetricsAutoConfiguration {
|
||||
.formatted(JvmCpuMeterConventions.class.getSimpleName()));
|
||||
}
|
||||
return (deprecatedConventions != null) ? new ProcessorMetrics(Collections.emptyList(), deprecatedConventions)
|
||||
: factory.create();
|
||||
: factory.create(observationProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -103,9 +106,12 @@ public final class SystemMetricsAutoConfiguration {
|
||||
return this.cpuTimeConvention != null || this.cpuCountConvention != null || this.cpuLoadConvention != null;
|
||||
}
|
||||
|
||||
ProcessorMetrics create() {
|
||||
PropertyMapper map = PropertyMapper.get();
|
||||
ProcessorMetrics create(ObservationProperties observationProperties) {
|
||||
ProcessorMetrics.Builder builder = ProcessorMetrics.builder();
|
||||
if (observationProperties.getConventions() == ConventionsVariant.OPENTELEMETRY) {
|
||||
builder.openTelemetryConventions();
|
||||
}
|
||||
PropertyMapper map = PropertyMapper.get();
|
||||
map.from(this.cpuTimeConvention).to(builder::cpuTimeConvention);
|
||||
map.from(this.cpuCountConvention).to(builder::cpuCountConvention);
|
||||
map.from(this.cpuLoadConvention).to(builder::cpuLoadConvention);
|
||||
|
||||
+77
@@ -33,12 +33,22 @@ import io.micrometer.core.instrument.binder.jvm.convention.JvmClassUnloadedMeter
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.JvmMemoryCommittedMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.JvmMemoryMaxMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.JvmMemoryMeterConventions;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.JvmMemoryUsedAfterLastGcMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.JvmMemoryUsedMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.JvmThreadCountMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.JvmThreadMeterConventions;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.micrometer.MicrometerJvmClassCountMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.micrometer.MicrometerJvmClassLoadingMeterConventions;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.micrometer.MicrometerJvmMemoryMeterConventions;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.micrometer.MicrometerJvmMemoryUsedAfterLastGcMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.micrometer.MicrometerJvmMemoryUsedMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.micrometer.MicrometerJvmThreadCountMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.micrometer.MicrometerJvmThreadMeterConventions;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.otel.OpenTelemetryJvmClassCountMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.otel.OpenTelemetryJvmMemoryCommittedMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.otel.OpenTelemetryJvmMemoryUsedAfterLastGcMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.otel.OpenTelemetryJvmMemoryUsedMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.otel.OpenTelemetryJvmThreadCountMeterConvention;
|
||||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledForJreRange;
|
||||
@@ -80,6 +90,62 @@ class JvmMetricsAutoConfigurationTests {
|
||||
this.contextRunner.run(assertMetricsBeans());
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfiguresJvmMetricsWithDefaultConventions() {
|
||||
this.contextRunner.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JvmMemoryMetrics.class)
|
||||
.getBean(JvmMemoryMetrics.class)
|
||||
.extracting("memoryUsedConvention")
|
||||
.isInstanceOf(MicrometerJvmMemoryUsedMeterConvention.class);
|
||||
assertThat(context.getBean(JvmMemoryMetrics.class)).extracting("memoryUsedAfterLastGcConvention")
|
||||
.isInstanceOf(MicrometerJvmMemoryUsedAfterLastGcMeterConvention.class);
|
||||
assertThat(context).hasSingleBean(JvmThreadMetrics.class)
|
||||
.getBean(JvmThreadMetrics.class)
|
||||
.extracting("threadCountConvention")
|
||||
.isInstanceOf(MicrometerJvmThreadCountMeterConvention.class);
|
||||
assertThat(context).hasSingleBean(ClassLoaderMetrics.class)
|
||||
.getBean(ClassLoaderMetrics.class)
|
||||
.extracting("classCountConvention")
|
||||
.isInstanceOf(MicrometerJvmClassCountMeterConvention.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfiguresJvmMetricsWithOpenTelemetryConventions() {
|
||||
this.contextRunner.withPropertyValues("management.observations.conventions=opentelemetry").run((context) -> {
|
||||
assertThat(context).hasSingleBean(JvmMemoryMetrics.class)
|
||||
.getBean(JvmMemoryMetrics.class)
|
||||
.extracting("memoryUsedConvention")
|
||||
.isInstanceOf(OpenTelemetryJvmMemoryUsedMeterConvention.class);
|
||||
assertThat(context.getBean(JvmMemoryMetrics.class)).extracting("memoryUsedAfterLastGcConvention")
|
||||
.isInstanceOf(OpenTelemetryJvmMemoryUsedAfterLastGcMeterConvention.class);
|
||||
assertThat(context).hasSingleBean(JvmThreadMetrics.class)
|
||||
.getBean(JvmThreadMetrics.class)
|
||||
.extracting("threadCountConvention")
|
||||
.isInstanceOf(OpenTelemetryJvmThreadCountMeterConvention.class);
|
||||
assertThat(context).hasSingleBean(ClassLoaderMetrics.class)
|
||||
.getBean(ClassLoaderMetrics.class)
|
||||
.extracting("classCountConvention")
|
||||
.isInstanceOf(OpenTelemetryJvmClassCountMeterConvention.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void allowsIndividualConventionToBeOverriddenWithOpenTelemetryConventions() {
|
||||
JvmMemoryUsedMeterConvention customUsed = mock(JvmMemoryUsedMeterConvention.class);
|
||||
this.contextRunner.withPropertyValues("management.observations.conventions=opentelemetry")
|
||||
.withBean(JvmMemoryUsedMeterConvention.class, () -> customUsed)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JvmMemoryMetrics.class)
|
||||
.getBean(JvmMemoryMetrics.class)
|
||||
.hasFieldOrPropertyWithValue("memoryUsedConvention", customUsed);
|
||||
assertThat(context.getBean(JvmMemoryMetrics.class)).extracting("memoryCommittedConvention")
|
||||
.isInstanceOf(OpenTelemetryJvmMemoryCommittedMeterConvention.class);
|
||||
assertThat(context.getBean(JvmMemoryMetrics.class)).extracting("memoryUsedAfterLastGcConvention")
|
||||
.isInstanceOf(OpenTelemetryJvmMemoryUsedAfterLastGcMeterConvention.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void allowsCustomJvmGcMetricsToBeUsed() {
|
||||
this.contextRunner.withUserConfiguration(CustomJvmGcMetricsConfiguration.class)
|
||||
@@ -171,6 +237,17 @@ class JvmMetricsAutoConfigurationTests {
|
||||
.hasFieldOrPropertyWithValue("memoryMaxConvention", memoryMaxConvention));
|
||||
}
|
||||
|
||||
@Test
|
||||
void allowCustomJvmMemoryUsedAfterLastGcMeterConventionToBeUsed() {
|
||||
JvmMemoryUsedAfterLastGcMeterConvention memoryUsedAfterLastGcConvention = mock(
|
||||
JvmMemoryUsedAfterLastGcMeterConvention.class);
|
||||
this.contextRunner
|
||||
.withBean(JvmMemoryUsedAfterLastGcMeterConvention.class, () -> memoryUsedAfterLastGcConvention)
|
||||
.run((context) -> assertThat(context).hasSingleBean(JvmMemoryMetrics.class)
|
||||
.getBean(JvmMemoryMetrics.class)
|
||||
.hasFieldOrPropertyWithValue("memoryUsedAfterLastGcConvention", memoryUsedAfterLastGcConvention));
|
||||
}
|
||||
|
||||
@Test
|
||||
void allowsCustomJvmThreadMetricsToBeUsed() {
|
||||
this.contextRunner.withUserConfiguration(CustomJvmThreadMetricsConfiguration.class)
|
||||
|
||||
+32
@@ -25,7 +25,10 @@ import io.micrometer.core.instrument.binder.jvm.convention.JvmCpuCountMeterConve
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.JvmCpuLoadMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.JvmCpuMeterConventions;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.JvmCpuTimeMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.micrometer.MicrometerJvmCpuCountMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.micrometer.MicrometerJvmCpuMeterConventions;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.otel.OpenTelemetryJvmCpuCountMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.jvm.convention.otel.OpenTelemetryJvmCpuLoadMeterConvention;
|
||||
import io.micrometer.core.instrument.binder.system.FileDescriptorMetrics;
|
||||
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
|
||||
import io.micrometer.core.instrument.binder.system.UptimeMetrics;
|
||||
@@ -73,6 +76,35 @@ class SystemMetricsAutoConfigurationTests {
|
||||
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ProcessorMetrics.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfiguresProcessorMetricsWithDefaultConventions() {
|
||||
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ProcessorMetrics.class)
|
||||
.getBean(ProcessorMetrics.class)
|
||||
.extracting("cpuCountConvention")
|
||||
.isInstanceOf(MicrometerJvmCpuCountMeterConvention.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfiguresProcessorMetricsWithOpenTelemetryConventions() {
|
||||
this.contextRunner.withPropertyValues("management.observations.conventions=opentelemetry")
|
||||
.run((context) -> assertThat(context).hasSingleBean(ProcessorMetrics.class)
|
||||
.getBean(ProcessorMetrics.class)
|
||||
.extracting("cpuCountConvention")
|
||||
.isInstanceOf(OpenTelemetryJvmCpuCountMeterConvention.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void allowsIndividualConventionToBeOverriddenWithOpenTelemetryConventions() {
|
||||
JvmCpuCountMeterConvention customCount = mock(JvmCpuCountMeterConvention.class);
|
||||
this.contextRunner.withPropertyValues("management.observations.conventions=opentelemetry")
|
||||
.withBean(JvmCpuCountMeterConvention.class, () -> customCount)
|
||||
.run((context) -> assertThat(context).hasSingleBean(ProcessorMetrics.class)
|
||||
.getBean(ProcessorMetrics.class)
|
||||
.hasFieldOrPropertyWithValue("cpuCountConvention", customCount)
|
||||
.extracting("cpuLoadConvention")
|
||||
.isInstanceOf(OpenTelemetryJvmCpuLoadMeterConvention.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void allowsCustomProcessorMetricsToBeUsed() {
|
||||
this.contextRunner.withUserConfiguration(CustomProcessorMetricsConfiguration.class)
|
||||
|
||||
+19
@@ -45,6 +45,11 @@ public class ObservationProperties {
|
||||
*/
|
||||
private Map<String, Boolean> enable = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Conventions variant to use when configuring observations and metrics.
|
||||
*/
|
||||
private ConventionsVariant conventions = ConventionsVariant.MICROMETER;
|
||||
|
||||
public Map<String, Boolean> getEnable() {
|
||||
return this.enable;
|
||||
}
|
||||
@@ -65,6 +70,20 @@ public class ObservationProperties {
|
||||
this.keyValues = keyValues;
|
||||
}
|
||||
|
||||
public ConventionsVariant getConventions() {
|
||||
return this.conventions;
|
||||
}
|
||||
|
||||
public void setConventions(ConventionsVariant conventions) {
|
||||
this.conventions = conventions;
|
||||
}
|
||||
|
||||
public enum ConventionsVariant {
|
||||
|
||||
OPENTELEMETRY, MICROMETER,
|
||||
|
||||
}
|
||||
|
||||
public static class Http {
|
||||
|
||||
private final Client client = new Client();
|
||||
|
||||
+12
-1
@@ -25,6 +25,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@@ -34,6 +35,7 @@ import org.springframework.boot.micrometer.observation.autoconfigure.Observation
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention;
|
||||
import org.springframework.http.server.reactive.observation.OpenTelemetryServerRequestObservationConvention;
|
||||
import org.springframework.http.server.reactive.observation.ServerRequestObservationConvention;
|
||||
|
||||
/**
|
||||
@@ -71,9 +73,18 @@ public final class WebFluxObservationAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ServerRequestObservationConvention.class)
|
||||
DefaultServerRequestObservationConvention defaultServerRequestObservationConvention() {
|
||||
@ConditionalOnProperty(name = "management.observations.conventions", havingValue = "micrometer",
|
||||
matchIfMissing = true)
|
||||
DefaultServerRequestObservationConvention micrometerServerRequestObservationConvention() {
|
||||
return new DefaultServerRequestObservationConvention(
|
||||
this.observationProperties.getHttp().getServer().getRequests().getName());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ServerRequestObservationConvention.class)
|
||||
@ConditionalOnProperty(name = "management.observations.conventions", havingValue = "opentelemetry")
|
||||
OpenTelemetryServerRequestObservationConvention openTelemetryServerRequestObservationConvention() {
|
||||
return new OpenTelemetryServerRequestObservationConvention();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
@@ -32,6 +32,7 @@ import org.springframework.boot.test.context.runner.ReactiveWebApplicationContex
|
||||
import org.springframework.boot.test.system.CapturedOutput;
|
||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||
import org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention;
|
||||
import org.springframework.http.server.reactive.observation.OpenTelemetryServerRequestObservationConvention;
|
||||
import org.springframework.http.server.reactive.observation.ServerRequestObservationConvention;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@@ -107,6 +108,12 @@ class WebFluxObservationAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void openTelemetryConventionConfiguredViaProperties() {
|
||||
this.contextRunner.withPropertyValues("management.observations.conventions=opentelemetry")
|
||||
.run((context) -> assertThat(context).hasSingleBean(OpenTelemetryServerRequestObservationConvention.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBackOffOnCustomServerRequestObservationConvention() {
|
||||
this.contextRunner
|
||||
|
||||
+21
-6
@@ -21,12 +21,13 @@ import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import jakarta.servlet.DispatcherType;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingFilterBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@@ -39,6 +40,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
|
||||
import org.springframework.http.server.observation.OpenTelemetryServerRequestObservationConvention;
|
||||
import org.springframework.http.server.observation.ServerRequestObservationConvention;
|
||||
import org.springframework.web.filter.ServerHttpObservationFilter;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
@@ -64,13 +66,26 @@ import org.springframework.web.servlet.DispatcherServlet;
|
||||
public final class WebMvcObservationAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingFilterBean
|
||||
FilterRegistrationBean<ServerHttpObservationFilter> webMvcObservationFilter(ObservationRegistry registry,
|
||||
ObjectProvider<ServerRequestObservationConvention> customConvention,
|
||||
@ConditionalOnMissingBean(ServerRequestObservationConvention.class)
|
||||
@ConditionalOnProperty(name = "management.observations.conventions", havingValue = "micrometer",
|
||||
matchIfMissing = true)
|
||||
DefaultServerRequestObservationConvention micrometerServerRequestObservationConvention(
|
||||
ObservationProperties observationProperties) {
|
||||
String name = observationProperties.getHttp().getServer().getRequests().getName();
|
||||
ServerRequestObservationConvention convention = customConvention
|
||||
.getIfAvailable(() -> new DefaultServerRequestObservationConvention(name));
|
||||
return new DefaultServerRequestObservationConvention(name);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ServerRequestObservationConvention.class)
|
||||
@ConditionalOnProperty(name = "management.observations.conventions", havingValue = "opentelemetry")
|
||||
OpenTelemetryServerRequestObservationConvention openTelemetryServerRequestObservationConvention() {
|
||||
return new OpenTelemetryServerRequestObservationConvention();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingFilterBean
|
||||
FilterRegistrationBean<ServerHttpObservationFilter> webMvcObservationFilter(ObservationRegistry registry,
|
||||
ServerRequestObservationConvention convention) {
|
||||
ServerHttpObservationFilter filter = new ServerHttpObservationFilter(registry, convention);
|
||||
FilterRegistrationBean<ServerHttpObservationFilter> registration = new FilterRegistrationBean<>(filter);
|
||||
registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
|
||||
|
||||
+13
@@ -45,6 +45,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
|
||||
import org.springframework.http.server.observation.OpenTelemetryServerRequestObservationConvention;
|
||||
import org.springframework.test.web.servlet.assertj.MockMvcTester;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@@ -92,6 +93,18 @@ class WebMvcObservationAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultMicrometerConvention() {
|
||||
this.contextRunner
|
||||
.run((context) -> assertThat(context).hasSingleBean(DefaultServerRequestObservationConvention.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void openTelemetryConventionConfiguredViaProperties() {
|
||||
this.contextRunner.withPropertyValues("management.observations.conventions=opentelemetry")
|
||||
.run((context) -> assertThat(context).hasSingleBean(OpenTelemetryServerRequestObservationConvention.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void customConventionWhenPresent() {
|
||||
this.contextRunner.withUserConfiguration(CustomConventionConfiguration.class)
|
||||
|
||||
Reference in New Issue
Block a user