Add noop tracer bean if tracing is disabled

The new functionality doesn't expose any public API
and works the same as WebTestClientContextCustomizer
does.

Closes gh-32907
This commit is contained in:
Moritz Halbritter
2022-11-02 12:07:01 +01:00
parent 7aff82009f
commit 85fd475821
4 changed files with 179 additions and 18 deletions

View File

@@ -19,17 +19,36 @@ 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.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.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
* unless {@link AutoConfigureObservability} is set on the test class.
* <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
@@ -44,11 +63,10 @@ class ObservabilityContextCustomizerFactory implements ContextCustomizerFactory
if (annotation == null) {
return new DisableObservabilityContextCustomizer(true, true);
}
return new DisableObservabilityContextCustomizer(!annotation.metrics(), !annotation.tracing());
}
static class DisableObservabilityContextCustomizer implements ContextCustomizer {
private static class DisableObservabilityContextCustomizer implements ContextCustomizer {
private final boolean disableMetrics;
@@ -68,9 +86,29 @@ class ObservabilityContextCustomizerFactory implements ContextCustomizerFactory
}
if (this.disableTracing) {
TestPropertyValues.of("management.tracing.enabled=false").applyTo(context);
registerNoopTracer(context);
}
}
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) {
@@ -90,4 +128,40 @@ 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.
*/
private 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(Tracer.class, () -> Tracer.NOOP));
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
}