Create spring-boot-micrometer-observation module

This commit is contained in:
Andy Wilkinson
2025-05-29 10:57:44 +01:00
committed by Phillip Webb
parent bf644c9d55
commit 3ea65eff08
53 changed files with 612 additions and 327 deletions

View File

@@ -100,6 +100,7 @@ include "spring-boot-project:spring-boot-liquibase"
include "spring-boot-project:spring-boot-neo4j"
include "spring-boot-project:spring-boot-mail"
include "spring-boot-project:spring-boot-metrics"
include "spring-boot-project:spring-boot-micrometer-observation"
include "spring-boot-project:spring-boot-mongodb"
include "spring-boot-project:spring-boot-mustache"
include "spring-boot-project:spring-boot-netty"

View File

@@ -39,6 +39,7 @@ dependencies {
optional(project(":spring-boot-project:spring-boot-jsonb"))
optional(project(":spring-boot-project:spring-boot-kafka"))
optional(project(":spring-boot-project:spring-boot-metrics"))
optional(project(":spring-boot-project:spring-boot-micrometer-observation"))
optional(project(":spring-boot-project:spring-boot-opentelemetry"))
optional(project(":spring-boot-project:spring-boot-r2dbc"))
optional(project(":spring-boot-project:spring-boot-restclient"))

View File

@@ -23,8 +23,6 @@ import org.glassfish.jersey.micrometer.server.ObservationApplicationEventListene
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -34,6 +32,8 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.jersey.autoconfigure.ResourceConfigCustomizer;
import org.springframework.boot.metrics.OnlyOnceLoggingDenyMeterFilter;
import org.springframework.boot.metrics.autoconfigure.MetricsProperties;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.observation;
package org.springframework.boot.actuate.autoconfigure.observability;
import java.util.List;
@@ -22,32 +22,24 @@ import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler;
import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler.IgnoredMeters;
import io.micrometer.core.instrument.observation.MeterObservationHandler;
import io.micrometer.observation.GlobalObservationConvention;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationFilter;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationPredicate;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.aop.ObservedAspect;
import io.micrometer.tracing.Tracer;
import io.micrometer.tracing.handler.TracingAwareMeterObservationHandler;
import io.micrometer.tracing.handler.TracingObservationHandler;
import org.aspectj.weaver.Advice;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration;
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.ConditionalOnBooleanProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.metrics.autoconfigure.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationHandlerGrouping;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
/**
* {@link EnableAutoConfiguration Auto-configuration} for the Micrometer Observation API.
@@ -58,34 +50,12 @@ import org.springframework.core.annotation.Order;
* @author Vedran Pavic
* @since 3.0.0
*/
@AutoConfiguration(after = { CompositeMeterRegistryAutoConfiguration.class, MicrometerTracingAutoConfiguration.class })
@AutoConfiguration(
beforeName = "org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration",
afterName = "org.springframework.boot.metrics.autoconfigure.CompositeMeterRegistryAutoConfiguration",
after = MicrometerTracingAutoConfiguration.class)
@ConditionalOnClass(ObservationRegistry.class)
@EnableConfigurationProperties(ObservationProperties.class)
public class ObservationAutoConfiguration {
@Bean
static ObservationRegistryPostProcessor observationRegistryPostProcessor(
ObjectProvider<ObservationRegistryCustomizer<?>> observationRegistryCustomizers,
ObjectProvider<ObservationPredicate> observationPredicates,
ObjectProvider<GlobalObservationConvention<?>> observationConventions,
ObjectProvider<ObservationHandler<?>> observationHandlers,
ObjectProvider<ObservationHandlerGrouping> observationHandlerGrouping,
ObjectProvider<ObservationFilter> observationFilters) {
return new ObservationRegistryPostProcessor(observationRegistryCustomizers, observationPredicates,
observationConventions, observationHandlers, observationHandlerGrouping, observationFilters);
}
@Bean
@ConditionalOnMissingBean
ObservationRegistry observationRegistry() {
return ObservationRegistry.create();
}
@Bean
@Order(0)
PropertiesObservationFilterPredicate propertiesObservationFilter(ObservationProperties properties) {
return new PropertiesObservationFilterPredicate(properties);
}
public class ObservabilityAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(MeterRegistry.class)
@@ -126,6 +96,7 @@ public class ObservationAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(MeterRegistry.class)
@ConditionalOnMissingBean(MeterObservationHandler.class)
@EnableConfigurationProperties(ObservationProperties.class)
static class MeterObservationHandlerConfiguration {
@ConditionalOnMissingBean(type = "io.micrometer.tracing.Tracer")
@@ -158,17 +129,4 @@ public class ObservationAutoConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Advice.class)
@ConditionalOnBooleanProperty("management.observations.annotations.enabled")
static class ObservedAspectConfiguration {
@Bean
@ConditionalOnMissingBean
ObservedAspect observedAspect(ObservationRegistry observationRegistry) {
return new ObservedAspect(observationRegistry);
}
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Auto-configuration for observability (metrics and tracing).
*/
package org.springframework.boot.actuate.autoconfigure.observability;

View File

@@ -19,12 +19,12 @@ package org.springframework.boot.actuate.autoconfigure.observation.batch;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.batch.core.configuration.annotation.BatchObservabilityBeanPostProcessor;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
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.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.context.annotation.Bean;
/**

View File

@@ -21,12 +21,12 @@ import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
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.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.graphql.execution.GraphQlSource;
import org.springframework.graphql.observation.DataFetcherObservationConvention;

View File

@@ -21,8 +21,6 @@ import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -32,6 +30,8 @@ import org.springframework.boot.metrics.OnlyOnceLoggingDenyMeterFilter;
import org.springframework.boot.metrics.autoconfigure.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.metrics.autoconfigure.MetricsProperties;
import org.springframework.boot.metrics.autoconfigure.MetricsProperties.Web.Client;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

View File

@@ -19,9 +19,9 @@ package org.springframework.boot.actuate.autoconfigure.observation.web.client;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties;
import org.springframework.boot.restclient.RestClientCustomizer;
import org.springframework.boot.restclient.actuate.observation.ObservationRestClientCustomizer;
import org.springframework.context.annotation.Bean;

View File

@@ -19,9 +19,9 @@ package org.springframework.boot.actuate.autoconfigure.observation.web.client;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties;
import org.springframework.boot.restclient.RestTemplateBuilder;
import org.springframework.boot.restclient.actuate.observation.ObservationRestTemplateCustomizer;
import org.springframework.context.annotation.Bean;

View File

@@ -19,9 +19,9 @@ package org.springframework.boot.actuate.autoconfigure.observation.web.client;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.metrics.autoconfigure.MetricsProperties;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties;
import org.springframework.boot.webclient.actuate.observation.ObservationWebClientCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

View File

@@ -21,8 +21,6 @@ import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -33,6 +31,8 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.metrics.OnlyOnceLoggingDenyMeterFilter;
import org.springframework.boot.metrics.autoconfigure.MetricsProperties;
import org.springframework.boot.metrics.autoconfigure.export.simple.SimpleMetricsExportAutoConfiguration;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention;

View File

@@ -23,8 +23,6 @@ import io.micrometer.observation.ObservationRegistry;
import jakarta.servlet.DispatcherType;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -37,6 +35,8 @@ import org.springframework.boot.metrics.autoconfigure.CompositeMeterRegistryAuto
import org.springframework.boot.metrics.autoconfigure.MetricsAutoConfiguration;
import org.springframework.boot.metrics.autoconfigure.MetricsProperties;
import org.springframework.boot.metrics.autoconfigure.export.simple.SimpleMetricsExportAutoConfiguration;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

View File

@@ -25,12 +25,12 @@ import io.r2dbc.spi.ConnectionFactory;
import io.r2dbc.spi.ConnectionFactoryOptions;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
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.context.properties.EnableConfigurationProperties;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.r2dbc.OptionsCapableConnectionFactory;
import org.springframework.boot.r2dbc.autoconfigure.ProxyConnectionFactoryCustomizer;
import org.springframework.context.annotation.Bean;

View File

@@ -18,11 +18,11 @@ package org.springframework.boot.actuate.autoconfigure.scheduling;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
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.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

View File

@@ -308,12 +308,6 @@
"level": "error"
}
},
{
"name": "management.observations.annotations.enabled",
"type": "java.lang.Boolean",
"description": "Whether auto-configuration of Micrometer annotations is enabled.",
"defaultValue": false
},
{
"name": "management.otlp.logging.export.enabled",
"type": "java.lang.Boolean",

View File

@@ -16,9 +16,9 @@ org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAuto
org.springframework.boot.actuate.autoconfigure.metrics.data.RepositoryMetricsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.jersey.JerseyServerMetricsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.metrics.task.TaskExecutorMetricsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.observability.ObservabilityAutoConfiguration
org.springframework.boot.actuate.autoconfigure.observation.batch.BatchObservationAutoConfiguration
org.springframework.boot.actuate.autoconfigure.observation.graphql.GraphQlObservationAutoConfiguration
org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration
org.springframework.boot.actuate.autoconfigure.observation.web.client.HttpClientObservationsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.observation.web.reactive.WebFluxObservationAutoConfiguration
org.springframework.boot.actuate.autoconfigure.observation.web.servlet.WebMvcObservationAutoConfiguration

View File

@@ -20,6 +20,9 @@ import java.net.URI;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler;
import io.micrometer.core.instrument.observation.MeterObservationHandler;
import io.micrometer.observation.Observation.Context;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
@@ -28,12 +31,12 @@ import org.glassfish.jersey.server.ResourceConfig;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.jersey.autoconfigure.JerseyAutoConfiguration;
import org.springframework.boot.jersey.autoconfigure.ResourceConfigCustomizer;
import org.springframework.boot.metrics.autoconfigure.MetricsAutoConfiguration;
import org.springframework.boot.metrics.autoconfigure.export.simple.SimpleMetricsExportAutoConfiguration;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
@@ -80,7 +83,7 @@ class JerseyServerMetricsAutoConfigurationTests {
@Test
void httpRequestsAreTimed() {
this.webContextRunner.run((context) -> {
this.webContextRunner.withUserConfiguration(MetricsConfiguration.class).run((context) -> {
doRequest(context);
Thread.sleep(500);
MeterRegistry registry = context.getBean(MeterRegistry.class);
@@ -128,4 +131,14 @@ class JerseyServerMetricsAutoConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
static class MetricsConfiguration {
@Bean
MeterObservationHandler<Context> meterObservationHandler(MeterRegistry registry) {
return new DefaultMeterObservationHandler(registry);
}
}
}

View File

@@ -34,7 +34,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observability.ObservabilityAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.web.client.HttpClientObservationsAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.web.reactive.WebFluxObservationAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.web.servlet.WebMvcObservationAutoConfiguration;
@@ -47,6 +47,7 @@ import org.springframework.boot.metrics.autoconfigure.MetricsAutoConfiguration;
import org.springframework.boot.metrics.autoconfigure.jvm.JvmMetricsAutoConfiguration;
import org.springframework.boot.metrics.autoconfigure.logging.logback.LogbackMetricsAutoConfiguration;
import org.springframework.boot.metrics.autoconfigure.system.SystemMetricsAutoConfiguration;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.restclient.RestTemplateBuilder;
import org.springframework.boot.restclient.autoconfigure.RestTemplateAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
@@ -143,14 +144,14 @@ class MetricsIntegrationTests {
@Configuration(proxyBeanMethods = false)
@ImportAutoConfiguration({ MetricsAutoConfiguration.class, ObservationAutoConfiguration.class,
JvmMetricsAutoConfiguration.class, LogbackMetricsAutoConfiguration.class,
SystemMetricsAutoConfiguration.class, DataSourcePoolMetricsAutoConfiguration.class,
HibernateMetricsAutoConfiguration.class, HttpClientObservationsAutoConfiguration.class,
WebFluxObservationAutoConfiguration.class, WebMvcObservationAutoConfiguration.class,
JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
RestTemplateAutoConfiguration.class, WebMvcAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, TomcatServletWebServerAutoConfiguration.class,
TomcatServletWebServerAutoConfiguration.class })
ObservabilityAutoConfiguration.class, JvmMetricsAutoConfiguration.class,
LogbackMetricsAutoConfiguration.class, SystemMetricsAutoConfiguration.class,
DataSourcePoolMetricsAutoConfiguration.class, HibernateMetricsAutoConfiguration.class,
HttpClientObservationsAutoConfiguration.class, WebFluxObservationAutoConfiguration.class,
WebMvcObservationAutoConfiguration.class, JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, RestTemplateAutoConfiguration.class,
WebMvcAutoConfiguration.class, DispatcherServletAutoConfiguration.class,
TomcatServletWebServerAutoConfiguration.class, TomcatServletWebServerAutoConfiguration.class })
@Import(PersonController.class)
static class MetricsApp {

View File

@@ -14,36 +14,31 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.observation;
package org.springframework.boot.actuate.autoconfigure.observability;
import java.util.ArrayList;
import java.util.List;
import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler;
import io.micrometer.core.instrument.observation.MeterObservationHandler;
import io.micrometer.core.instrument.search.MeterNotFoundException;
import io.micrometer.observation.GlobalObservationConvention;
import io.micrometer.observation.Observation;
import io.micrometer.observation.Observation.Context;
import io.micrometer.observation.ObservationFilter;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationHandler.AllMatchingCompositeObservationHandler;
import io.micrometer.observation.ObservationHandler.FirstMatchingCompositeObservationHandler;
import io.micrometer.observation.ObservationPredicate;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.aop.ObservedAspect;
import io.micrometer.tracing.Tracer;
import io.micrometer.tracing.handler.TracingAwareMeterObservationHandler;
import io.micrometer.tracing.handler.TracingObservationHandler;
import org.aspectj.weaver.Advice;
import org.junit.jupiter.api.Test;
import org.mockito.Answers;
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationHandlerGrouping;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
@@ -53,52 +48,29 @@ import org.springframework.core.annotation.Order;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link ObservationAutoConfiguration}.
* Tests for {@link ObservabilityAutoConfiguration}.
*
* @author Moritz Halbritter
* @author Jonatan Ivanov
* @author Vedran Pavic
*/
class ObservationAutoConfigurationTests {
class ObservabilityAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().with(MetricsRun.simple())
.withPropertyValues("management.observations.annotations.enabled=true")
.withClassLoader(new FilteredClassLoader("io.micrometer.tracing"))
.withConfiguration(AutoConfigurations.of(ObservationAutoConfiguration.class));
.withConfiguration(
AutoConfigurations.of(ObservationAutoConfiguration.class, ObservabilityAutoConfiguration.class));
private final ApplicationContextRunner tracingContextRunner = new ApplicationContextRunner()
.with(MetricsRun.simple())
.withPropertyValues("management.observations.annotations.enabled=true")
.withUserConfiguration(TracerConfiguration.class)
.withConfiguration(AutoConfigurations.of(ObservationAutoConfiguration.class));
@Test
void beansShouldNotBeSuppliedWhenMicrometerObservationIsNotOnClassPath() {
this.tracingContextRunner.withClassLoader(new FilteredClassLoader("io.micrometer.observation"))
.run((context) -> {
assertThat(context).hasSingleBean(MeterRegistry.class);
assertThat(context).doesNotHaveBean(ObservationRegistry.class);
assertThat(context).doesNotHaveBean(ObservationHandler.class);
assertThat(context).doesNotHaveBean(ObservedAspect.class);
assertThat(context).doesNotHaveBean(ObservationHandlerGrouping.class);
});
}
@Test
void supplyObservationRegistryWhenMicrometerCoreAndTracingAreNotOnClassPath() {
this.contextRunner.withClassLoader(new FilteredClassLoader("io.micrometer.core", "io.micrometer.tracing"))
.run((context) -> {
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
Observation.start("test-observation", observationRegistry).stop();
assertThat(context).doesNotHaveBean(ObservationHandler.class);
assertThat(context).hasSingleBean(ObservedAspect.class);
assertThat(context).doesNotHaveBean(ObservationHandlerGrouping.class);
});
}
.withConfiguration(
AutoConfigurations.of(ObservationAutoConfiguration.class, ObservabilityAutoConfiguration.class));
@Test
void supplyMeterHandlerAndGroupingWhenMicrometerCoreIsOnClassPathButTracingIsNot() {
@@ -144,7 +116,8 @@ class ObservationAutoConfigurationTests {
void supplyMeterHandlerAndGroupingWhenMicrometerCoreAndTracingAreOnClassPathButThereIsNoTracer() {
new ApplicationContextRunner().with(MetricsRun.simple())
.withPropertyValues("management.observations.annotations.enabled=true")
.withConfiguration(AutoConfigurations.of(ObservationAutoConfiguration.class))
.withConfiguration(
AutoConfigurations.of(ObservationAutoConfiguration.class, ObservabilityAutoConfiguration.class))
.run((context) -> {
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
Observation.start("test-observation", observationRegistry).stop();
@@ -178,77 +151,6 @@ class ObservationAutoConfigurationTests {
.run((context) -> assertThat(context).doesNotHaveBean(ObservationHandler.class));
}
@Test
void allowsObservedAspectToBeDisabled() {
this.contextRunner.withClassLoader(new FilteredClassLoader(Advice.class))
.run((context) -> assertThat(context).doesNotHaveBean(ObservedAspect.class));
}
@Test
void allowsObservedAspectToBeDisabledWithProperty() {
this.contextRunner.withPropertyValues("management.observations.annotations.enabled=false")
.run((context) -> assertThat(context).doesNotHaveBean(ObservedAspect.class));
}
@Test
void allowsObservedAspectToBeCustomized() {
this.contextRunner.withUserConfiguration(CustomObservedAspectConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(ObservedAspect.class)
.getBean(ObservedAspect.class)
.isSameAs(context.getBean("customObservedAspect")));
}
@Test
void autoConfiguresObservationPredicates() {
this.contextRunner.withUserConfiguration(ObservationPredicates.class).run((context) -> {
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
// This is allowed by ObservationPredicates.customPredicate
Observation.start("observation1", observationRegistry).stop();
// This isn't allowed by ObservationPredicates.customPredicate
Observation.start("observation2", observationRegistry).stop();
MeterRegistry meterRegistry = context.getBean(MeterRegistry.class);
assertThat(meterRegistry.get("observation1").timer().count()).isOne();
assertThatExceptionOfType(MeterNotFoundException.class)
.isThrownBy(() -> meterRegistry.get("observation2").timer());
});
}
@Test
void autoConfiguresObservationFilters() {
this.contextRunner.withUserConfiguration(ObservationFilters.class).run((context) -> {
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
Observation.start("filtered", observationRegistry).stop();
MeterRegistry meterRegistry = context.getBean(MeterRegistry.class);
assertThat(meterRegistry.get("filtered").tag("filter", "one").timer().count()).isOne();
});
}
@Test
void shouldSupplyPropertiesObservationFilterBean() {
this.contextRunner
.run((context) -> assertThat(context).hasSingleBean(PropertiesObservationFilterPredicate.class));
}
@Test
void shouldApplyCommonKeyValuesToObservations() {
this.contextRunner.withPropertyValues("management.observations.key-values.a=alpha").run((context) -> {
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
Observation.start("keyvalues", observationRegistry).stop();
MeterRegistry meterRegistry = context.getBean(MeterRegistry.class);
assertThat(meterRegistry.get("keyvalues").tag("a", "alpha").timer().count()).isOne();
});
}
@Test
void autoConfiguresGlobalObservationConventions() {
this.contextRunner.withUserConfiguration(CustomGlobalObservationConvention.class).run((context) -> {
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
Context micrometerContext = new Context();
Observation.start("test-observation", () -> micrometerContext, observationRegistry).stop();
assertThat(micrometerContext.getAllKeyValues()).containsExactly(KeyValue.of("key1", "value1"));
});
}
@Test
void autoConfiguresObservationHandlers() {
this.contextRunner.withUserConfiguration(ObservationHandlers.class).run((context) -> {
@@ -323,27 +225,6 @@ class ObservationAutoConfigurationTests {
});
}
@Test
void shouldNotDisableSpringSecurityObservationsByDefault() {
this.contextRunner.run((context) -> {
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
Observation.start("spring.security.filterchains", observationRegistry).stop();
MeterRegistry meterRegistry = context.getBean(MeterRegistry.class);
assertThat(meterRegistry.get("spring.security.filterchains").timer().count()).isOne();
});
}
@Test
void shouldDisableSpringSecurityObservationsIfPropertyIsSet() {
this.contextRunner.withPropertyValues("management.observations.enable.spring.security=false").run((context) -> {
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
Observation.start("spring.security.filterchains", observationRegistry).stop();
MeterRegistry meterRegistry = context.getBean(MeterRegistry.class);
assertThatExceptionOfType(MeterNotFoundException.class)
.isThrownBy(() -> meterRegistry.get("spring.security.filterchains").timer());
});
}
@Test
void shouldEnableLongTaskTimersByDefault() {
this.contextRunner.run((context) -> {
@@ -384,63 +265,6 @@ class ObservationAutoConfigurationTests {
});
}
@Configuration(proxyBeanMethods = false)
static class ObservationPredicates {
@Bean
ObservationPredicate customPredicate() {
return (s, context) -> s.equals("observation1");
}
}
@Configuration(proxyBeanMethods = false)
static class ObservationFilters {
@Bean
@Order(1)
ObservationFilter observationFilterOne() {
return (context) -> context.addLowCardinalityKeyValue(KeyValue.of("filter", "one"));
}
@Bean
@Order(0)
ObservationFilter observationFilterTwo() {
return (context) -> context.addLowCardinalityKeyValue(KeyValue.of("filter", "two"));
}
}
@Configuration(proxyBeanMethods = false)
static class CustomObservedAspectConfiguration {
@Bean
ObservedAspect customObservedAspect(ObservationRegistry observationRegistry) {
return new ObservedAspect(observationRegistry);
}
}
@Configuration(proxyBeanMethods = false)
static class CustomGlobalObservationConvention {
@Bean
GlobalObservationConvention<?> customConvention() {
return new GlobalObservationConvention<>() {
@Override
public boolean supportsContext(Context context) {
return true;
}
@Override
public KeyValues getLowCardinalityKeyValues(Context context) {
return KeyValues.of("key1", "value1");
}
};
}
}
@Configuration(proxyBeanMethods = false)
@Import(CalledHandlersConfiguration.class)
static class ObservationHandlers {

View File

@@ -18,14 +18,17 @@ package org.springframework.boot.actuate.autoconfigure.observation.web.client;
import io.micrometer.common.KeyValues;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler;
import io.micrometer.core.instrument.observation.MeterObservationHandler;
import io.micrometer.observation.Observation.Context;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.tck.TestObservationRegistry;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.restclient.actuate.observation.ObservationRestClientCustomizer;
import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.restclient.test.MockServerRestClientCustomizer;
@@ -102,6 +105,7 @@ class RestClientObservationConfigurationTests {
@Test
void afterMaxUrisReachedFurtherUrisAreDenied(CapturedOutput output) {
this.contextRunner.with(MetricsRun.simple())
.withUserConfiguration(MetricsConfiguration.class)
.withPropertyValues("management.metrics.web.client.max-uri-tags=2")
.run((context) -> {
RestClientWithMockServer restClientWithMockServer = buildRestClientAndMockServer(context);
@@ -167,4 +171,14 @@ class RestClientObservationConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
static class MetricsConfiguration {
@Bean
MeterObservationHandler<Context> meterObservationHandler(MeterRegistry registry) {
return new DefaultMeterObservationHandler(registry);
}
}
}

View File

@@ -21,8 +21,8 @@ import io.micrometer.observation.tck.TestObservationRegistry;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.boot.restclient.test.MockServerRestClientCustomizer;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;

View File

@@ -18,14 +18,17 @@ package org.springframework.boot.actuate.autoconfigure.observation.web.client;
import io.micrometer.common.KeyValues;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler;
import io.micrometer.core.instrument.observation.MeterObservationHandler;
import io.micrometer.observation.Observation.Context;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.tck.TestObservationRegistry;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.restclient.RestTemplateBuilder;
import org.springframework.boot.restclient.actuate.observation.ObservationRestTemplateCustomizer;
import org.springframework.boot.restclient.autoconfigure.RestTemplateAutoConfiguration;
@@ -100,6 +103,7 @@ class RestTemplateObservationConfigurationTests {
@Test
void afterMaxUrisReachedFurtherUrisAreDenied(CapturedOutput output) {
this.contextRunner.with(MetricsRun.simple())
.withUserConfiguration(MetricsConfiguration.class)
.withPropertyValues("management.metrics.web.client.max-uri-tags=2")
.run((context) -> {
RestTemplate restTemplate = context.getBean(RestTemplateBuilder.class).build();
@@ -153,4 +157,14 @@ class RestTemplateObservationConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
static class MetricsConfiguration {
@Bean
MeterObservationHandler<Context> meterObservationHandler(MeterRegistry registry) {
return new DefaultMeterObservationHandler(registry);
}
}
}

View File

@@ -21,8 +21,8 @@ import io.micrometer.observation.tck.TestObservationRegistry;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.restclient.RestTemplateBuilder;
import org.springframework.boot.restclient.autoconfigure.RestTemplateAutoConfiguration;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;

View File

@@ -20,6 +20,9 @@ import java.time.Duration;
import io.micrometer.common.KeyValues;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler;
import io.micrometer.core.instrument.observation.MeterObservationHandler;
import io.micrometer.observation.Observation.Context;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.tck.TestObservationRegistry;
import org.junit.jupiter.api.Test;
@@ -27,8 +30,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
import reactor.core.publisher.Mono;
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.system.CapturedOutput;
@@ -97,28 +100,32 @@ class WebClientObservationConfigurationTests {
@Test
void afterMaxUrisReachedFurtherUrisAreDenied(CapturedOutput output) {
this.contextRunner.withPropertyValues("management.metrics.web.client.max-uri-tags=2").run((context) -> {
TestObservationRegistry registry = getInitializedRegistry(context);
assertThat(registry).hasNumberOfObservationsWithNameEqualTo("http.client.requests", 3);
MeterRegistry meterRegistry = context.getBean(MeterRegistry.class);
assertThat(meterRegistry.find("http.client.requests").timers()).hasSize(1);
// MeterFilter.maximumAllowableTags() works with prefix matching.
assertThat(meterRegistry.find("http.client.requests.active").longTaskTimers()).hasSize(1);
assertThat(output).contains("Reached the maximum number of URI tags for 'http.client.requests'.")
.contains("Are you using 'uriVariables'?");
});
this.contextRunner.withUserConfiguration(MetricsConfiguration.class)
.withPropertyValues("management.metrics.web.client.max-uri-tags=2")
.run((context) -> {
TestObservationRegistry registry = getInitializedRegistry(context);
assertThat(registry).hasNumberOfObservationsWithNameEqualTo("http.client.requests", 3);
MeterRegistry meterRegistry = context.getBean(MeterRegistry.class);
assertThat(meterRegistry.find("http.client.requests").timers()).hasSize(1);
// MeterFilter.maximumAllowableTags() works with prefix matching.
assertThat(meterRegistry.find("http.client.requests.active").longTaskTimers()).hasSize(1);
assertThat(output).contains("Reached the maximum number of URI tags for 'http.client.requests'.")
.contains("Are you using 'uriVariables'?");
});
}
@Test
void shouldNotDenyNorLogIfMaxUrisIsNotReached(CapturedOutput output) {
this.contextRunner.withPropertyValues("management.metrics.web.client.max-uri-tags=5").run((context) -> {
TestObservationRegistry registry = getInitializedRegistry(context);
assertThat(registry).hasNumberOfObservationsWithNameEqualTo("http.client.requests", 3);
MeterRegistry meterRegistry = context.getBean(MeterRegistry.class);
assertThat(meterRegistry.find("http.client.requests").timers()).hasSize(3);
assertThat(output).doesNotContain("Reached the maximum number of URI tags for 'http.client.requests'.")
.doesNotContain("Are you using 'uriVariables'?");
});
this.contextRunner.withUserConfiguration(MetricsConfiguration.class)
.withPropertyValues("management.metrics.web.client.max-uri-tags=5")
.run((context) -> {
TestObservationRegistry registry = getInitializedRegistry(context);
assertThat(registry).hasNumberOfObservationsWithNameEqualTo("http.client.requests", 3);
MeterRegistry meterRegistry = context.getBean(MeterRegistry.class);
assertThat(meterRegistry.find("http.client.requests").timers()).hasSize(3);
assertThat(output).doesNotContain("Reached the maximum number of URI tags for 'http.client.requests'.")
.doesNotContain("Are you using 'uriVariables'?");
});
}
private TestObservationRegistry getInitializedRegistry(AssertableApplicationContext context) {
@@ -172,4 +179,14 @@ class WebClientObservationConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
static class MetricsConfiguration {
@Bean
MeterObservationHandler<Context> meterObservationHandler(MeterRegistry registry) {
return new DefaultMeterObservationHandler(registry);
}
}
}

View File

@@ -25,9 +25,9 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
import org.springframework.boot.actuate.autoconfigure.metrics.web.TestController;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.metrics.autoconfigure.MetricsAutoConfiguration;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.boot.test.system.CapturedOutput;

View File

@@ -19,6 +19,10 @@ package org.springframework.boot.actuate.autoconfigure.observation.web.servlet;
import java.util.EnumSet;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler;
import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler.IgnoredMeters;
import io.micrometer.core.instrument.observation.MeterObservationHandler;
import io.micrometer.observation.Observation.Context;
import io.micrometer.observation.tck.TestObservationRegistry;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter;
@@ -27,9 +31,9 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
import org.springframework.boot.actuate.autoconfigure.metrics.web.TestController;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.metrics.autoconfigure.MetricsAutoConfiguration;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.test.system.CapturedOutput;
@@ -64,7 +68,7 @@ class WebMvcObservationAutoConfigurationTests {
.withConfiguration(AutoConfigurations.of(WebMvcObservationAutoConfiguration.class));
@Test
void backsOffWhenMeterRegistryIsMissing() {
void backsOffWhenObservationRegistryIsMissing() {
new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(WebMvcObservationAutoConfiguration.class))
.run((context) -> assertThat(context).doesNotHaveBean(FilterRegistrationBean.class));
@@ -128,7 +132,7 @@ class WebMvcObservationAutoConfigurationTests {
@Test
void afterMaxUrisReachedFurtherUrisAreDenied(CapturedOutput output) {
this.contextRunner.withUserConfiguration(TestController.class)
this.contextRunner.withUserConfiguration(TestController.class, MetricsConfiguration.class)
.withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class, ObservationAutoConfiguration.class,
WebMvcAutoConfiguration.class))
.withPropertyValues("management.metrics.web.server.max-uri-tags=2")
@@ -141,7 +145,7 @@ class WebMvcObservationAutoConfigurationTests {
@Test
void afterMaxUrisReachedFurtherUrisAreDeniedWhenUsingCustomObservationName(CapturedOutput output) {
this.contextRunner.withUserConfiguration(TestController.class)
this.contextRunner.withUserConfiguration(TestController.class, MetricsConfiguration.class)
.withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class, ObservationAutoConfiguration.class,
WebMvcAutoConfiguration.class))
.withPropertyValues("management.metrics.web.server.max-uri-tags=2",
@@ -155,7 +159,7 @@ class WebMvcObservationAutoConfigurationTests {
@Test
void shouldNotDenyNorLogIfMaxUrisIsNotReached(CapturedOutput output) {
this.contextRunner.withUserConfiguration(TestController.class)
this.contextRunner.withUserConfiguration(TestController.class, MetricsConfiguration.class)
.withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class, ObservationAutoConfiguration.class,
WebMvcAutoConfiguration.class))
.withPropertyValues("management.metrics.web.server.max-uri-tags=5")
@@ -237,4 +241,14 @@ class WebMvcObservationAutoConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
static class MetricsConfiguration {
@Bean
MeterObservationHandler<Context> meterObservationHandler(MeterRegistry registry) {
return new DefaultMeterObservationHandler(registry, IgnoredMeters.LONG_TASK_TIMER);
}
}
}

View File

@@ -21,11 +21,11 @@ import java.util.List;
import io.micrometer.observation.ObservationRegistry;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.scheduling.ScheduledTasksObservabilityAutoConfiguration.ObservabilitySchedulingConfigurer;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.context.annotation.ImportCandidates;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

View File

@@ -50,10 +50,10 @@ import org.assertj.core.api.Assertions;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.BraveAutoConfigurationTests.SpanHandlerConfiguration.AdditionalSpanHandler;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.context.properties.IncompatibleConfigurationException;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;

View File

@@ -60,9 +60,9 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Mockito;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.context.annotation.Configurations;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.testsupport.classpath.ForkedClassPath;

View File

@@ -46,10 +46,10 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpTracingAutoConfigurationIntegrationTests.MockGrpcServer.RecordedGrpcRequest;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;

View File

@@ -20,19 +20,23 @@ import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
import io.micrometer.tracing.Tracer;
import io.micrometer.tracing.handler.TracingAwareMeterObservationHandler;
import io.prometheus.metrics.expositionformats.OpenMetricsTextFormatWriter;
import io.prometheus.metrics.tracer.common.SpanContext;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.BraveAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.metrics.autoconfigure.export.prometheus.PrometheusMetricsExportAutoConfiguration;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
@@ -85,8 +89,9 @@ class PrometheusExemplarsAutoConfigurationTests {
@Test
void prometheusOpenMetricsOutputWithoutExemplarsOnHistogramCount() {
this.contextRunner.withPropertyValues(
"management.prometheus.metrics.export.properties.io.prometheus.exporter.exemplarsOnAllMetricTypes=false")
this.contextRunner.withUserConfiguration(TracingConfiguration.class)
.withPropertyValues(
"management.prometheus.metrics.export.properties.io.prometheus.exporter.exemplarsOnAllMetricTypes=false")
.run((context) -> {
assertThat(context).hasSingleBean(SpanContext.class);
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
@@ -112,7 +117,7 @@ class PrometheusExemplarsAutoConfigurationTests {
@Test
void prometheusOpenMetricsOutputShouldContainExemplars() {
this.contextRunner.run((context) -> {
this.contextRunner.withUserConfiguration(TracingConfiguration.class).run((context) -> {
assertThat(context).hasSingleBean(SpanContext.class);
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
Observation.start("test.observation", observationRegistry).stop();
@@ -157,4 +162,16 @@ class PrometheusExemplarsAutoConfigurationTests {
private record TraceInfo(String traceId, String spanId) {
}
@Configuration(proxyBeanMethods = false)
static class TracingConfiguration {
@Bean
TracingAwareMeterObservationHandler<Observation.Context> tracingAwareMeterObservationHandler(
MeterRegistry meterRegistry, Tracer tracer) {
DefaultMeterObservationHandler delegate = new DefaultMeterObservationHandler(meterRegistry);
return new TracingAwareMeterObservationHandler<>(delegate, tracer);
}
}
}

View File

@@ -2039,6 +2039,7 @@ bom {
"spring-boot-loader-tools",
"spring-boot-mail",
"spring-boot-metrics",
"spring-boot-micrometer-observation",
"spring-boot-mongodb",
"spring-boot-mustache",
"spring-boot-neo4j",

View File

@@ -108,6 +108,7 @@ dependencies {
autoConfiguration(project(path: ":spring-boot-project:spring-boot-liquibase", configuration: "autoConfigurationMetadata"))
autoConfiguration(project(path: ":spring-boot-project:spring-boot-mail", configuration: "autoConfigurationMetadata"))
autoConfiguration(project(path: ":spring-boot-project:spring-boot-metrics", configuration: "autoConfigurationMetadata"))
autoConfiguration(project(path: ":spring-boot-project:spring-boot-micrometer-observation", configuration: "autoConfigurationMetadata"))
autoConfiguration(project(path: ":spring-boot-project:spring-boot-mongodb", configuration: "autoConfigurationMetadata"))
autoConfiguration(project(path: ":spring-boot-project:spring-boot-mustache", configuration: "autoConfigurationMetadata"))
autoConfiguration(project(path: ":spring-boot-project:spring-boot-neo4j", configuration: "autoConfigurationMetadata"))
@@ -193,6 +194,7 @@ dependencies {
configurationProperties(project(path: ":spring-boot-project:spring-boot-liquibase", configuration: "configurationPropertiesMetadata"))
configurationProperties(project(path: ":spring-boot-project:spring-boot-mail", configuration: "configurationPropertiesMetadata"))
configurationProperties(project(path: ":spring-boot-project:spring-boot-metrics", configuration: "configurationPropertiesMetadata"))
configurationProperties(project(path: ":spring-boot-project:spring-boot-micrometer-observation", configuration: "configurationPropertiesMetadata"))
configurationProperties(project(path: ":spring-boot-project:spring-boot-mongodb", configuration: "configurationPropertiesMetadata"))
configurationProperties(project(path: ":spring-boot-project:spring-boot-mustache", configuration: "configurationPropertiesMetadata"))
configurationProperties(project(path: ":spring-boot-project:spring-boot-neo4j", configuration: "configurationPropertiesMetadata"))

View File

@@ -11,6 +11,7 @@ description = "Spring Boot Metrics"
dependencies {
api(project(":spring-boot-project:spring-boot"))
api(project(":spring-boot-project:spring-boot-micrometer-observation"))
api("io.micrometer:micrometer-core")
optional(project(":spring-boot-project:spring-boot-actuator-autoconfigure"))

View File

@@ -0,0 +1,21 @@
plugins {
id "java-library"
id "org.springframework.boot.auto-configuration"
id "org.springframework.boot.configuration-properties"
id "org.springframework.boot.deployed"
id "org.springframework.boot.optional-dependencies"
}
description = "Spring Boot New Project"
dependencies {
api(project(":spring-boot-project:spring-boot"))
optional(project(":spring-boot-project:spring-boot-autoconfigure"))
optional("org.aspectj:aspectjweaver")
testImplementation(project(":spring-boot-project:spring-boot-test"))
testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support"))
testRuntimeOnly("ch.qos.logback:logback-classic")
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.micrometer.observation.autoconfigure;
import io.micrometer.observation.GlobalObservationConvention;
import io.micrometer.observation.ObservationFilter;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationPredicate;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.aop.ObservedAspect;
import org.aspectj.weaver.Advice;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
/**
* {@link EnableAutoConfiguration Auto-configuration} for the Micrometer Observation API.
*
* @author Moritz Halbritter
* @author Brian Clozel
* @author Jonatan Ivanov
* @author Vedran Pavic
* @since 4.0.0
*/
@AutoConfiguration
@ConditionalOnClass(ObservationRegistry.class)
@EnableConfigurationProperties(ObservationProperties.class)
public class ObservationAutoConfiguration {
@Bean
static ObservationRegistryPostProcessor observationRegistryPostProcessor(
ObjectProvider<ObservationRegistryCustomizer<?>> observationRegistryCustomizers,
ObjectProvider<ObservationPredicate> observationPredicates,
ObjectProvider<GlobalObservationConvention<?>> observationConventions,
ObjectProvider<ObservationHandler<?>> observationHandlers,
ObjectProvider<ObservationHandlerGrouping> observationHandlerGrouping,
ObjectProvider<ObservationFilter> observationFilters) {
return new ObservationRegistryPostProcessor(observationRegistryCustomizers, observationPredicates,
observationConventions, observationHandlers, observationHandlerGrouping, observationFilters);
}
@Bean
@ConditionalOnMissingBean
ObservationRegistry observationRegistry() {
return ObservationRegistry.create();
}
@Bean
@Order(0)
PropertiesObservationFilterPredicate propertiesObservationFilter(ObservationProperties properties) {
return new PropertiesObservationFilterPredicate(properties);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Advice.class)
@ConditionalOnBooleanProperty("management.observations.annotations.enabled")
static class ObservedAspectConfiguration {
@Bean
@ConditionalOnMissingBean
ObservedAspect observedAspect(ObservationRegistry observationRegistry) {
return new ObservedAspect(observationRegistry);
}
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.observation;
package org.springframework.boot.micrometer.observation.autoconfigure;
import java.util.ArrayList;
import java.util.List;
@@ -32,17 +32,18 @@ import org.springframework.util.MultiValueMap;
*
* @author Andy Wilkinson
* @author Moritz Halbritter
* @since 4.0.0
*/
@SuppressWarnings("rawtypes")
class ObservationHandlerGrouping {
public class ObservationHandlerGrouping {
private final List<Class<? extends ObservationHandler>> categories;
ObservationHandlerGrouping(Class<? extends ObservationHandler> category) {
public ObservationHandlerGrouping(Class<? extends ObservationHandler> category) {
this(List.of(category));
}
ObservationHandlerGrouping(List<Class<? extends ObservationHandler>> categories) {
public ObservationHandlerGrouping(List<Class<? extends ObservationHandler>> categories) {
this.categories = categories;
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.observation;
package org.springframework.boot.micrometer.observation.autoconfigure;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -27,7 +27,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
*
* @author Brian Clozel
* @author Moritz Halbritter
* @since 3.0.0
* @since 4.0.0
*/
@ConfigurationProperties("management.observations")
public class ObservationProperties {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.observation;
package org.springframework.boot.micrometer.observation.autoconfigure;
import java.util.List;
@@ -74,8 +74,14 @@ class ObservationRegistryConfigurer {
}
private void registerHandlers(ObservationRegistry registry) {
this.observationHandlerGrouping.ifAvailable(
(grouping) -> grouping.apply(asOrderedList(this.observationHandlers), registry.observationConfig()));
ObservationHandlerGrouping grouping = this.observationHandlerGrouping.getIfAvailable();
List<ObservationHandler<?>> orderedHandlers = asOrderedList(this.observationHandlers);
if (grouping != null) {
grouping.apply(orderedHandlers, registry.observationConfig());
}
else {
orderedHandlers.forEach((handler) -> registry.observationConfig().observationHandler(handler));
}
}
private void registerObservationPredicates(ObservationRegistry registry) {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.observation;
package org.springframework.boot.micrometer.observation.autoconfigure;
import io.micrometer.observation.ObservationRegistry;
@@ -24,7 +24,7 @@ import io.micrometer.observation.ObservationRegistry;
*
* @param <T> the registry type to customize
* @author Moritz Halbritter
* @since 3.0.0
* @since 4.0.0
*/
@FunctionalInterface
public interface ObservationRegistryCustomizer<T extends ObservationRegistry> {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.observation;
package org.springframework.boot.micrometer.observation.autoconfigure;
import io.micrometer.observation.GlobalObservationConvention;
import io.micrometer.observation.ObservationFilter;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.observation;
package org.springframework.boot.micrometer.observation.autoconfigure;
import java.util.Map;
import java.util.Map.Entry;

View File

@@ -15,6 +15,6 @@
*/
/**
* Auto-configuration for the Micrometer Observation API.
* Auto-configuration for Micrometer Observation.
*/
package org.springframework.boot.actuate.autoconfigure.observation;
package org.springframework.boot.micrometer.observation.autoconfigure;

View File

@@ -0,0 +1,11 @@
{
"groups": [],
"properties": [
{
"name": "management.observations.annotations.enabled",
"type": "java.lang.Boolean",
"description": "Whether auto-configuration of Micrometer annotations is enabled.",
"defaultValue": false
}
]
}

View File

@@ -0,0 +1 @@
org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration

View File

@@ -0,0 +1,256 @@
/*
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.micrometer.observation.autoconfigure;
import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
import io.micrometer.observation.GlobalObservationConvention;
import io.micrometer.observation.Observation;
import io.micrometer.observation.Observation.Context;
import io.micrometer.observation.ObservationFilter;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationHandler.AllMatchingCompositeObservationHandler;
import io.micrometer.observation.ObservationHandler.FirstMatchingCompositeObservationHandler;
import io.micrometer.observation.ObservationPredicate;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.aop.ObservedAspect;
import org.aspectj.weaver.Advice;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ObservationAutoConfiguration}.
*
* @author Moritz Halbritter
* @author Jonatan Ivanov
* @author Vedran Pavic
*/
class ObservationAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withPropertyValues("management.observations.annotations.enabled=true")
.withConfiguration(AutoConfigurations.of(ObservationAutoConfiguration.class))
.withUserConfiguration(ObservationHandlers.class);
@Test
void beansShouldNotBeSuppliedWhenMicrometerObservationIsNotOnClassPath() {
this.contextRunner.withClassLoader(new FilteredClassLoader("io.micrometer.observation")).run((context) -> {
assertThat(context).doesNotHaveBean(ObservationRegistry.class);
assertThat(context).doesNotHaveBean(ObservedAspect.class);
});
}
@Test
void supplyObservationRegistry() {
this.contextRunner.run((context) -> {
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
Observation.start("test-observation", observationRegistry).stop();
assertThat(context).hasSingleBean(ObservedAspect.class);
assertThat(context).doesNotHaveBean(ObservationHandlerGrouping.class);
});
}
@Test
void allowsObservedAspectToBeDisabled() {
this.contextRunner.withClassLoader(new FilteredClassLoader(Advice.class))
.run((context) -> assertThat(context).doesNotHaveBean(ObservedAspect.class));
}
@Test
void allowsObservedAspectToBeDisabledWithProperty() {
this.contextRunner.withPropertyValues("management.observations.annotations.enabled=false")
.run((context) -> assertThat(context).doesNotHaveBean(ObservedAspect.class));
}
@Test
void allowsObservedAspectToBeCustomized() {
this.contextRunner.withUserConfiguration(CustomObservedAspectConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(ObservedAspect.class)
.getBean(ObservedAspect.class)
.isSameAs(context.getBean("customObservedAspect")));
}
@Test
void autoConfiguresObservationPredicates() {
this.contextRunner.withUserConfiguration(ObservationPredicates.class).run((context) -> {
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
// This is allowed by ObservationPredicates.customPredicate
Observation observation = Observation.start("observation1", observationRegistry);
assertThat(observation).isNotEqualTo(Observation.NOOP);
observation.stop();
// This isn't allowed by ObservationPredicates.customPredicate
observation = Observation.start("observation2", observationRegistry);
assertThat(observation).isEqualTo(Observation.NOOP);
observation.stop();
});
}
@Test
void autoConfiguresObservationFilters() {
this.contextRunner.withUserConfiguration(ObservationFilters.class).run((context) -> {
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
Observation observation = Observation.start("filtered", observationRegistry);
observation.stop();
observation.getContext().getLowCardinalityKeyValues().forEach((kv) -> System.out.println(kv.getKey()));
assertThat(observation.getContext().getLowCardinalityKeyValue("filter").getValue()).isEqualTo("one");
});
}
@Test
void shouldSupplyPropertiesObservationFilterBean() {
this.contextRunner
.run((context) -> assertThat(context).hasSingleBean(PropertiesObservationFilterPredicate.class));
}
@Test
void shouldApplyCommonKeyValuesToObservations() {
this.contextRunner.withPropertyValues("management.observations.key-values.a=alpha").run((context) -> {
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
Observation observation = Observation.start("keyvalues", observationRegistry);
observation.stop();
assertThat(observation.getContext().getLowCardinalityKeyValue("a").getValue()).isEqualTo("alpha");
});
}
@Test
void autoConfiguresGlobalObservationConventions() {
this.contextRunner.withUserConfiguration(CustomGlobalObservationConvention.class).run((context) -> {
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
Context micrometerContext = new Context();
Observation.start("test-observation", () -> micrometerContext, observationRegistry).stop();
assertThat(micrometerContext.getAllKeyValues()).containsExactly(KeyValue.of("key1", "value1"));
});
}
@Test
void shouldNotDisableSpringSecurityObservationsByDefault() {
this.contextRunner.run((context) -> {
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
Observation observation = Observation.start("spring.security.filterchains", observationRegistry);
assertThat(observation).isNotEqualTo(Observation.NOOP);
observation.stop();
});
}
@Test
void shouldDisableSpringSecurityObservationsIfPropertyIsSet() {
this.contextRunner.withPropertyValues("management.observations.enable.spring.security=false").run((context) -> {
ObservationRegistry observationRegistry = context.getBean(ObservationRegistry.class);
Observation observation = Observation.start("spring.security.filterchains", observationRegistry);
assertThat(observation).isEqualTo(Observation.NOOP);
observation.stop();
});
}
@Configuration(proxyBeanMethods = false)
static class ObservationPredicates {
@Bean
ObservationPredicate customPredicate() {
return (s, context) -> s.equals("observation1");
}
}
@Configuration(proxyBeanMethods = false)
static class ObservationFilters {
@Bean
@Order(1)
ObservationFilter observationFilterOne() {
return (context) -> context.addLowCardinalityKeyValue(KeyValue.of("filter", "one"));
}
@Bean
@Order(0)
ObservationFilter observationFilterTwo() {
return (context) -> context.addLowCardinalityKeyValue(KeyValue.of("filter", "two"));
}
}
@Configuration(proxyBeanMethods = false)
static class CustomObservedAspectConfiguration {
@Bean
ObservedAspect customObservedAspect(ObservationRegistry observationRegistry) {
return new ObservedAspect(observationRegistry);
}
}
@Configuration(proxyBeanMethods = false)
static class CustomGlobalObservationConvention {
@Bean
GlobalObservationConvention<?> customConvention() {
return new GlobalObservationConvention<>() {
@Override
public boolean supportsContext(Context context) {
return true;
}
@Override
public KeyValues getLowCardinalityKeyValues(Context context) {
return KeyValues.of("key1", "value1");
}
};
}
}
@Configuration(proxyBeanMethods = false)
static class ObservationHandlers {
@Bean
@Order(4)
AllMatchingCompositeObservationHandler customAllMatchingCompositeObservationHandler() {
return new AllMatchingCompositeObservationHandler();
}
@Bean
@Order(3)
FirstMatchingCompositeObservationHandler customFirstMatchingCompositeObservationHandler() {
return new FirstMatchingCompositeObservationHandler();
}
@Bean
@Order(2)
ObservationHandler<Context> customObservationHandler() {
return new CustomObservationHandler();
}
}
private static final class CustomObservationHandler implements ObservationHandler<Context> {
@Override
public boolean supportsContext(Context context) {
return true;
}
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.observation;
package org.springframework.boot.micrometer.observation.autoconfigure;
import java.lang.reflect.Method;
import java.util.List;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.observation;
package org.springframework.boot.micrometer.observation.autoconfigure;
import java.util.ArrayList;
import java.util.List;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.observation;
package org.springframework.boot.micrometer.observation.autoconfigure;
import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;

View File

@@ -71,6 +71,7 @@ dependencies {
exclude(group: "org.liquibase")
}
optional(project(":spring-boot-project:spring-boot-metrics"))
optional(project(":spring-boot-project:spring-boot-micrometer-observation"))
optional(project(":spring-boot-project:spring-boot-mongodb"))
optional(project(":spring-boot-project:spring-boot-r2dbc"))
optional(project(":spring-boot-project:spring-boot-reactor-netty"))

View File

@@ -1,7 +1,7 @@
# AutoConfigureObservability auto-configuration imports
# Observation
org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration
org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration
# Metrics
org.springframework.boot.metrics.autoconfigure.export.simple.SimpleMetricsExportAutoConfiguration
@@ -11,3 +11,6 @@ org.springframework.boot.metrics.autoconfigure.MetricsAutoConfiguration
# Tracing
org.springframework.boot.actuate.autoconfigure.tracing.NoopTracerAutoConfiguration
org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration
# Observability
org.springframework.boot.actuate.autoconfigure.observability.ObservabilityAutoConfiguration

View File

@@ -57,6 +57,10 @@
<allow pkg="io.micrometer" />
</subpackage>
<subpackage name="micrometer">
<allow pkg="io.micrometer" />
</subpackage>
<subpackage name="[^.]+\.actuate" regex="true">
<subpackage name="endpoint">
<file name=".*Endpoint(WebExtension)?" regex="true">