Rework @AutoConfigureObservability and tracing auto-configurations
@ConditionalOnEnabledTracing is now applied to the minimal amount of beans. The beans which are annotated with it are beans that will lead to span sending to backends. This leaves the majority of the Micrometer Tracing, Brave and OpenTelemetry infrastructure untouched in tests. Closes gh-35354
This commit is contained in:
@@ -19,39 +19,19 @@ package org.springframework.boot.test.autoconfigure.actuate.observability;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.micrometer.tracing.Tracer;
|
||||
|
||||
import org.springframework.aot.AotDetector;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.TestContextAnnotationUtils;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizerFactory} that globally disables metrics export and tracing in
|
||||
* tests. The behaviour can be controlled with {@link AutoConfigureObservability} on the
|
||||
* test class or via the {@value #AUTO_CONFIGURE_PROPERTY} property.
|
||||
* <p>
|
||||
* Registers {@link Tracer#NOOP} if tracing is disabled, micrometer-tracing is on the
|
||||
* classpath, and the user hasn't supplied their own {@link Tracer}.
|
||||
*
|
||||
* @author Chris Bono
|
||||
* @author Moritz Halbritter
|
||||
@@ -87,7 +67,6 @@ class ObservabilityContextCustomizerFactory implements ContextCustomizerFactory
|
||||
}
|
||||
if (isTracingDisabled(context.getEnvironment())) {
|
||||
TestPropertyValues.of("management.tracing.enabled=false").applyTo(context);
|
||||
registerNoopTracer(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,25 +84,6 @@ class ObservabilityContextCustomizerFactory implements ContextCustomizerFactory
|
||||
return !environment.getProperty(AUTO_CONFIGURE_PROPERTY, Boolean.class, false);
|
||||
}
|
||||
|
||||
private void registerNoopTracer(ConfigurableApplicationContext context) {
|
||||
if (AotDetector.useGeneratedArtifacts()) {
|
||||
return;
|
||||
}
|
||||
if (!ClassUtils.isPresent("io.micrometer.tracing.Tracer", context.getClassLoader())) {
|
||||
return;
|
||||
}
|
||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||
if (beanFactory instanceof BeanDefinitionRegistry registry) {
|
||||
registerNoopTracer(registry);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerNoopTracer(BeanDefinitionRegistry registry) {
|
||||
RootBeanDefinition definition = new RootBeanDefinition(NoopTracerRegistrar.class);
|
||||
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
registry.registerBeanDefinition(NoopTracerRegistrar.class.getName(), definition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
@@ -143,54 +103,4 @@ class ObservabilityContextCustomizerFactory implements ContextCustomizerFactory
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link BeanDefinitionRegistryPostProcessor} that runs after the
|
||||
* {@link ConfigurationClassPostProcessor} and adds a {@link Tracer} bean definition
|
||||
* when a {@link Tracer} hasn't already been registered.
|
||||
*/
|
||||
static class NoopTracerRegistrar implements BeanDefinitionRegistryPostProcessor, Ordered, BeanFactoryAware {
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
|
||||
if (AotDetector.useGeneratedArtifacts()) {
|
||||
return;
|
||||
}
|
||||
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors((ListableBeanFactory) this.beanFactory,
|
||||
Tracer.class, false, false).length == 0) {
|
||||
registry.registerBeanDefinition("noopTracer", new RootBeanDefinition(NoopTracerFactoryBean.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NoopTracerFactoryBean implements FactoryBean<Tracer> {
|
||||
|
||||
@Override
|
||||
public Tracer getObject() {
|
||||
return Tracer.NOOP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return Tracer.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,22 +18,15 @@ package org.springframework.boot.test.autoconfigure.actuate.observability;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import io.micrometer.tracing.Tracer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.context.annotation.UserConfigurations;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link AutoConfigureObservability} and
|
||||
@@ -82,59 +75,6 @@ class ObservabilityContextCustomizerFactoryTests {
|
||||
assertThatTracingIsEnabled(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRegisterNoopTracerIfTracingIsDisabled() {
|
||||
ContextCustomizer customizer = createContextCustomizer(NoAnnotation.class);
|
||||
ConfigurableApplicationContext context = new GenericApplicationContext();
|
||||
applyCustomizerToContext(customizer, context);
|
||||
context.refresh();
|
||||
Tracer tracer = context.getBean(Tracer.class);
|
||||
assertThat(tracer).isNotNull();
|
||||
assertThat(tracer.nextSpan().isNoop()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotRegisterNoopTracerIfTracingIsEnabled() {
|
||||
ContextCustomizer customizer = createContextCustomizer(WithAnnotation.class);
|
||||
ConfigurableApplicationContext context = new GenericApplicationContext();
|
||||
applyCustomizerToContext(customizer, context);
|
||||
context.refresh();
|
||||
assertThat(context.getBeanProvider(Tracer.class).getIfAvailable()).as("Tracer bean").isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotRegisterNoopTracerIfMicrometerTracingIsNotPresent() throws Exception {
|
||||
try (FilteredClassLoader filteredClassLoader = new FilteredClassLoader("io.micrometer.tracing")) {
|
||||
ContextCustomizer customizer = createContextCustomizer(NoAnnotation.class);
|
||||
new ApplicationContextRunner().withClassLoader(filteredClassLoader)
|
||||
.withInitializer(applyCustomizer(customizer))
|
||||
.run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(Tracer.class);
|
||||
assertThatMetricsAreDisabled(context);
|
||||
assertThatTracingIsDisabled(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBackOffOnCustomTracer() {
|
||||
ContextCustomizer customizer = createContextCustomizer(NoAnnotation.class);
|
||||
new ApplicationContextRunner().withConfiguration(UserConfigurations.of(CustomTracer.class))
|
||||
.withInitializer(applyCustomizer(customizer))
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(Tracer.class);
|
||||
assertThat(context).hasBean("customTracer");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotRunIfAotIsEnabled() {
|
||||
ContextCustomizer customizer = createContextCustomizer(NoAnnotation.class);
|
||||
new ApplicationContextRunner().withSystemProperties("spring.aot.enabled:true")
|
||||
.withInitializer(applyCustomizer(customizer))
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(Tracer.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void notEquals() {
|
||||
ContextCustomizer customizer1 = createContextCustomizer(OnlyMetrics.class);
|
||||
@@ -256,14 +196,4 @@ class ObservabilityContextCustomizerFactoryTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomTracer {
|
||||
|
||||
@Bean
|
||||
Tracer customTracer() {
|
||||
return mock(Tracer.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user