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:
Moritz Halbritter
2023-06-16 14:09:00 +02:00
parent fb4b26a842
commit 27add2bbe3
21 changed files with 100 additions and 208 deletions

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}