Require micrometer-tracing-bridge-otel to auto-configure OTel

Closes gh-32503
pull/32525/head
Andy Wilkinson 2 years ago
parent 6cc3619675
commit 5449397463

@ -16,25 +16,138 @@
package org.springframework.boot.actuate.autoconfigure.tracing; package org.springframework.boot.actuate.autoconfigure.tracing;
import org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryConfigurations.MicrometerConfiguration; import java.util.List;
import org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryConfigurations.SdkConfiguration; import java.util.regex.Pattern;
import org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryConfigurations.TracerConfiguration; import java.util.stream.Collectors;
import io.micrometer.tracing.SamplerFunction;
import io.micrometer.tracing.otel.bridge.DefaultHttpClientAttributesGetter;
import io.micrometer.tracing.otel.bridge.DefaultHttpServerAttributesExtractor;
import io.micrometer.tracing.otel.bridge.OtelBaggageManager;
import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext;
import io.micrometer.tracing.otel.bridge.OtelHttpClientHandler;
import io.micrometer.tracing.otel.bridge.OtelHttpServerHandler;
import io.micrometer.tracing.otel.bridge.OtelTracer;
import io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
import io.opentelemetry.sdk.trace.SpanProcessor;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import org.springframework.boot.SpringBootVersion;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Import; 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.core.env.Environment;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for OpenTelemetry. * {@link EnableAutoConfiguration Auto-configuration} for OpenTelemetry.
* *
* It uses imports on {@link OpenTelemetryConfigurations} to guarantee the correct
* configuration ordering.
*
* @author Moritz Halbritter * @author Moritz Halbritter
* @since 3.0.0 * @since 3.0.0
*/ */
@AutoConfiguration(before = MicrometerTracingAutoConfiguration.class) @AutoConfiguration(before = MicrometerTracingAutoConfiguration.class)
@Import({ SdkConfiguration.class, TracerConfiguration.class, MicrometerConfiguration.class })
@ConditionalOnEnabledTracing @ConditionalOnEnabledTracing
@ConditionalOnClass({ OtelTracer.class, SdkTracerProvider.class, OpenTelemetry.class })
@EnableConfigurationProperties(TracingProperties.class)
public class OpenTelemetryAutoConfiguration { public class OpenTelemetryAutoConfiguration {
/**
* Default value for application name if {@code spring.application.name} is not set.
*/
private static final String DEFAULT_APPLICATION_NAME = "application";
@Bean
@ConditionalOnMissingBean
OpenTelemetry openTelemetry(SdkTracerProvider sdkTracerProvider, ContextPropagators contextPropagators) {
return OpenTelemetrySdk.builder().setTracerProvider(sdkTracerProvider).setPropagators(contextPropagators)
.build();
}
@Bean
@ConditionalOnMissingBean
SdkTracerProvider otelSdkTracerProvider(Environment environment, List<SpanProcessor> spanProcessors,
Sampler sampler) {
String applicationName = environment.getProperty("spring.application.name", DEFAULT_APPLICATION_NAME);
SdkTracerProviderBuilder builder = SdkTracerProvider.builder().setSampler(sampler)
.setResource(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, applicationName)));
for (SpanProcessor spanProcessor : spanProcessors) {
builder.addSpanProcessor(spanProcessor);
}
return builder.build();
}
@Bean
@ConditionalOnMissingBean
ContextPropagators otelContextPropagators(List<TextMapPropagator> textMapPropagators) {
return ContextPropagators.create(TextMapPropagator.composite(textMapPropagators));
}
@Bean
@ConditionalOnMissingBean
Sampler otelSampler(TracingProperties properties) {
return Sampler.traceIdRatioBased(properties.getSampling().getProbability());
}
@Bean
@ConditionalOnMissingBean
SpanProcessor otelSpanProcessor(List<SpanExporter> spanExporter) {
return SpanProcessor.composite(spanExporter.stream()
.map((exporter) -> BatchSpanProcessor.builder(exporter).build()).collect(Collectors.toList()));
}
@Bean
@ConditionalOnMissingBean
Tracer otelTracer(OpenTelemetry openTelemetry) {
return openTelemetry.getTracer("org.springframework.boot", SpringBootVersion.getVersion());
}
@Bean
@ConditionalOnMissingBean
OtelTracer micrometerOtelTracer(Tracer tracer, EventPublisher eventPublisher,
OtelCurrentTraceContext otelCurrentTraceContext) {
return new OtelTracer(tracer, otelCurrentTraceContext, eventPublisher,
new OtelBaggageManager(otelCurrentTraceContext, List.of(), List.of()));
}
@Bean
@ConditionalOnMissingBean
EventPublisher otelTracerEventPublisher() {
return (event) -> {
};
}
@Bean
@ConditionalOnMissingBean
OtelCurrentTraceContext otelCurrentTraceContext() {
return new OtelCurrentTraceContext();
}
@Bean
@ConditionalOnMissingBean
OtelHttpClientHandler otelHttpClientHandler(OpenTelemetry openTelemetry) {
return new OtelHttpClientHandler(openTelemetry, null, null, SamplerFunction.deferDecision(),
new DefaultHttpClientAttributesGetter());
}
@Bean
@ConditionalOnMissingBean
OtelHttpServerHandler otelHttpServerHandler(OpenTelemetry openTelemetry) {
return new OtelHttpServerHandler(openTelemetry, null, null, Pattern.compile(""),
new DefaultHttpServerAttributesExtractor());
}
} }

@ -1,173 +0,0 @@
/*
* Copyright 2012-2022 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.actuate.autoconfigure.tracing;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import io.micrometer.tracing.SamplerFunction;
import io.micrometer.tracing.otel.bridge.DefaultHttpClientAttributesGetter;
import io.micrometer.tracing.otel.bridge.DefaultHttpServerAttributesExtractor;
import io.micrometer.tracing.otel.bridge.OtelBaggageManager;
import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext;
import io.micrometer.tracing.otel.bridge.OtelHttpClientHandler;
import io.micrometer.tracing.otel.bridge.OtelHttpServerHandler;
import io.micrometer.tracing.otel.bridge.OtelTracer;
import io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
import io.opentelemetry.sdk.trace.SpanProcessor;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import org.springframework.boot.SpringBootVersion;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
/**
* Configurations for OpenTelemetry. Those are imported by
* {@link OpenTelemetryAutoConfiguration}.
*
* @author Moritz Halbritter
*/
class OpenTelemetryConfigurations {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(SdkTracerProvider.class)
@EnableConfigurationProperties(TracingProperties.class)
static class SdkConfiguration {
/**
* Default value for application name if {@code spring.application.name} is not
* set.
*/
private static final String DEFAULT_APPLICATION_NAME = "application";
@Bean
@ConditionalOnMissingBean
OpenTelemetry openTelemetry(SdkTracerProvider sdkTracerProvider, ContextPropagators contextPropagators) {
return OpenTelemetrySdk.builder().setTracerProvider(sdkTracerProvider).setPropagators(contextPropagators)
.build();
}
@Bean
@ConditionalOnMissingBean
SdkTracerProvider otelSdkTracerProvider(Environment environment, List<SpanProcessor> spanProcessors,
Sampler sampler) {
String applicationName = environment.getProperty("spring.application.name", DEFAULT_APPLICATION_NAME);
SdkTracerProviderBuilder builder = SdkTracerProvider.builder().setSampler(sampler)
.setResource(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, applicationName)));
for (SpanProcessor spanProcessor : spanProcessors) {
builder.addSpanProcessor(spanProcessor);
}
return builder.build();
}
@Bean
@ConditionalOnMissingBean
ContextPropagators otelContextPropagators(List<TextMapPropagator> textMapPropagators) {
return ContextPropagators.create(TextMapPropagator.composite(textMapPropagators));
}
@Bean
@ConditionalOnMissingBean
Sampler otelSampler(TracingProperties properties) {
return Sampler.traceIdRatioBased(properties.getSampling().getProbability());
}
@Bean
@ConditionalOnMissingBean
SpanProcessor otelSpanProcessor(List<SpanExporter> spanExporter) {
return SpanProcessor.composite(spanExporter.stream()
.map((exporter) -> BatchSpanProcessor.builder(exporter).build()).collect(Collectors.toList()));
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Tracer.class)
static class TracerConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(OpenTelemetry.class)
Tracer otelTracer(OpenTelemetry openTelemetry) {
return openTelemetry.getTracer("org.springframework.boot", SpringBootVersion.getVersion());
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(OtelTracer.class)
static class MicrometerConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(Tracer.class)
OtelTracer micrometerOtelTracer(Tracer tracer, EventPublisher eventPublisher,
OtelCurrentTraceContext otelCurrentTraceContext) {
return new OtelTracer(tracer, otelCurrentTraceContext, eventPublisher,
new OtelBaggageManager(otelCurrentTraceContext, List.of(), List.of()));
}
@Bean
@ConditionalOnMissingBean
EventPublisher otelTracerEventPublisher() {
return (event) -> {
};
}
@Bean
@ConditionalOnMissingBean
OtelCurrentTraceContext otelCurrentTraceContext() {
return new OtelCurrentTraceContext();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(OpenTelemetry.class)
OtelHttpClientHandler otelHttpClientHandler(OpenTelemetry openTelemetry) {
return new OtelHttpClientHandler(openTelemetry, null, null, SamplerFunction.deferDecision(),
new DefaultHttpClientAttributesGetter());
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(OpenTelemetry.class)
OtelHttpServerHandler otelHttpServerHandler(OpenTelemetry openTelemetry) {
return new OtelHttpServerHandler(openTelemetry, null, null, Pattern.compile(""),
new DefaultHttpServerAttributesExtractor());
}
}
}

@ -23,10 +23,14 @@ import io.micrometer.tracing.otel.bridge.OtelTracer;
import io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher; import io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher;
import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.SpanProcessor;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.Answers; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryConfigurations.MicrometerConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ApplicationContextRunner;
@ -37,49 +41,48 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
/** /**
* Tests for {@link MicrometerConfiguration}. * Tests for {@link OpenTelemetryAutoConfiguration}.
* *
* @author Moritz Halbritter * @author Moritz Halbritter
* @author Andy Wilkinson
*/ */
class OpenTelemetryConfigurationsMicrometerConfigurationTests { class OpenTelemetryAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MicrometerConfiguration.class)); .withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class));
@Test @Test
void shouldSupplyBeans() { void shouldSupplyBeans() {
this.contextRunner.withUserConfiguration(TracerConfiguration.class, OpenTelemetryConfiguration.class) this.contextRunner.run((context) -> {
.run((context) -> { assertThat(context).hasSingleBean(OtelTracer.class);
assertThat(context).hasSingleBean(OtelTracer.class); assertThat(context).hasSingleBean(EventPublisher.class);
assertThat(context).hasSingleBean(EventPublisher.class); assertThat(context).hasSingleBean(OtelCurrentTraceContext.class);
assertThat(context).hasSingleBean(OtelCurrentTraceContext.class); assertThat(context).hasSingleBean(OtelHttpClientHandler.class);
assertThat(context).hasSingleBean(OtelHttpClientHandler.class); assertThat(context).hasSingleBean(OtelHttpServerHandler.class);
assertThat(context).hasSingleBean(OtelHttpServerHandler.class); assertThat(context).hasSingleBean(OpenTelemetry.class);
}); assertThat(context).hasSingleBean(SdkTracerProvider.class);
} assertThat(context).hasSingleBean(ContextPropagators.class);
assertThat(context).hasSingleBean(Sampler.class);
@Test assertThat(context).hasSingleBean(SpanProcessor.class);
void shouldNotSupplyBeansIfMicrometerTracingBridgeOtelIsMissing() { assertThat(context).hasSingleBean(Tracer.class);
this.contextRunner.withClassLoader(new FilteredClassLoader("io.micrometer.tracing.otel")) });
.withUserConfiguration(TracerConfiguration.class, OpenTelemetryConfiguration.class).run((context) -> {
assertThat(context).doesNotHaveBean(OtelTracer.class);
assertThat(context).doesNotHaveBean(EventPublisher.class);
assertThat(context).doesNotHaveBean(OtelCurrentTraceContext.class);
assertThat(context).doesNotHaveBean(OtelHttpClientHandler.class);
assertThat(context).doesNotHaveBean(OtelHttpServerHandler.class);
});
}
@Test
void shouldNotSupplyBeansIfTracerIsMissing() {
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(OtelTracer.class));
} }
@Test @ParameterizedTest
void shouldNotSupplyBeansIfOpenTelemetryIsMissing() { @ValueSource(strings = { "io.micrometer.tracing.otel", "io.opentelemetry.sdk", "io.opentelemetry.api" })
this.contextRunner.withUserConfiguration(TracerConfiguration.class).run((context) -> { void shouldNotSupplyBeansIfDependencyIsMissing(String packageName) {
this.contextRunner.withClassLoader(new FilteredClassLoader(packageName)).run((context) -> {
assertThat(context).doesNotHaveBean(OtelTracer.class);
assertThat(context).doesNotHaveBean(EventPublisher.class);
assertThat(context).doesNotHaveBean(OtelCurrentTraceContext.class);
assertThat(context).doesNotHaveBean(OtelHttpClientHandler.class); assertThat(context).doesNotHaveBean(OtelHttpClientHandler.class);
assertThat(context).doesNotHaveBean(OtelHttpServerHandler.class); assertThat(context).doesNotHaveBean(OtelHttpServerHandler.class);
assertThat(context).doesNotHaveBean(OpenTelemetry.class);
assertThat(context).doesNotHaveBean(SdkTracerProvider.class);
assertThat(context).doesNotHaveBean(ContextPropagators.class);
assertThat(context).doesNotHaveBean(Sampler.class);
assertThat(context).doesNotHaveBean(SpanProcessor.class);
assertThat(context).doesNotHaveBean(Tracer.class);
}); });
} }
@ -96,6 +99,18 @@ class OpenTelemetryConfigurationsMicrometerConfigurationTests {
assertThat(context).hasSingleBean(OtelHttpClientHandler.class); assertThat(context).hasSingleBean(OtelHttpClientHandler.class);
assertThat(context).hasBean("customOtelHttpServerHandler"); assertThat(context).hasBean("customOtelHttpServerHandler");
assertThat(context).hasSingleBean(OtelHttpServerHandler.class); assertThat(context).hasSingleBean(OtelHttpServerHandler.class);
assertThat(context).hasBean("customOpenTelemetry");
assertThat(context).hasSingleBean(OpenTelemetry.class);
assertThat(context).hasBean("customSdkTracerProvider");
assertThat(context).hasSingleBean(SdkTracerProvider.class);
assertThat(context).hasBean("customContextPropagators");
assertThat(context).hasSingleBean(ContextPropagators.class);
assertThat(context).hasBean("customSampler");
assertThat(context).hasSingleBean(Sampler.class);
assertThat(context).hasBean("customSpanProcessor");
assertThat(context).hasSingleBean(SpanProcessor.class);
assertThat(context).hasBean("customTracer");
assertThat(context).hasSingleBean(Tracer.class);
}); });
} }
@ -127,24 +142,34 @@ class OpenTelemetryConfigurationsMicrometerConfigurationTests {
return mock(OtelHttpServerHandler.class); return mock(OtelHttpServerHandler.class);
} }
} @Bean
OpenTelemetry customOpenTelemetry() {
return mock(OpenTelemetry.class);
}
@Configuration(proxyBeanMethods = false) @Bean
private static class TracerConfiguration { SdkTracerProvider customSdkTracerProvider() {
return SdkTracerProvider.builder().build();
}
@Bean @Bean
Tracer tracer() { ContextPropagators customContextPropagators() {
return mock(Tracer.class); return mock(ContextPropagators.class);
} }
} @Bean
Sampler customSampler() {
return mock(Sampler.class);
}
@Configuration(proxyBeanMethods = false) @Bean
private static class OpenTelemetryConfiguration { SpanProcessor customSpanProcessor() {
return mock(SpanProcessor.class);
}
@Bean @Bean
OpenTelemetry openTelemetry() { Tracer customTracer() {
return mock(OpenTelemetry.class, Answers.RETURNS_MOCKS); return mock(Tracer.class);
} }
} }

@ -1,112 +0,0 @@
/*
* Copyright 2012-2022 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.actuate.autoconfigure.tracing;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.SpanProcessor;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryConfigurations.SdkConfiguration;
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 static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link SdkConfiguration}.
*
* @author Moritz Halbritter
*/
class OpenTelemetryConfigurationsSdkConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(SdkConfiguration.class));
@Test
void shouldSupplyBeans() {
this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(OpenTelemetry.class);
assertThat(context).hasSingleBean(SdkTracerProvider.class);
assertThat(context).hasSingleBean(ContextPropagators.class);
assertThat(context).hasSingleBean(Sampler.class);
assertThat(context).hasSingleBean(SpanProcessor.class);
});
}
@Test
void shouldBackOffOnCustomBeans() {
this.contextRunner.withUserConfiguration(CustomBeans.class).run((context) -> {
assertThat(context).hasBean("customOpenTelemetry");
assertThat(context).hasSingleBean(OpenTelemetry.class);
assertThat(context).hasBean("customSdkTracerProvider");
assertThat(context).hasSingleBean(SdkTracerProvider.class);
assertThat(context).hasBean("customContextPropagators");
assertThat(context).hasSingleBean(ContextPropagators.class);
assertThat(context).hasBean("customSampler");
assertThat(context).hasSingleBean(Sampler.class);
assertThat(context).hasBean("customSpanProcessor");
assertThat(context).hasSingleBean(SpanProcessor.class);
});
}
@Test
void shouldNotSupplyBeansIfSdkIsMissing() {
this.contextRunner.withClassLoader(new FilteredClassLoader("io.opentelemetry.sdk")).run((context) -> {
assertThat(context).doesNotHaveBean(OpenTelemetry.class);
assertThat(context).doesNotHaveBean(SdkTracerProvider.class);
assertThat(context).doesNotHaveBean(ContextPropagators.class);
assertThat(context).doesNotHaveBean(Sampler.class);
assertThat(context).doesNotHaveBean(SpanProcessor.class);
});
}
private static class CustomBeans {
@Bean
OpenTelemetry customOpenTelemetry() {
return mock(OpenTelemetry.class);
}
@Bean
SdkTracerProvider customSdkTracerProvider() {
return SdkTracerProvider.builder().build();
}
@Bean
ContextPropagators customContextPropagators() {
return mock(ContextPropagators.class);
}
@Bean
Sampler customSampler() {
return mock(Sampler.class);
}
@Bean
SpanProcessor customSpanProcessor() {
return mock(SpanProcessor.class);
}
}
}

@ -1,89 +0,0 @@
/*
* Copyright 2012-2022 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.actuate.autoconfigure.tracing;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Tracer;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryConfigurations.TracerConfiguration;
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 static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link TracerConfiguration}.
*
* @author Moritz Halbritter
*/
class OpenTelemetryConfigurationsTracerConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(TracerConfiguration.class));
@Test
void shouldSupplyBeans() {
this.contextRunner.withUserConfiguration(OpenTelemetryConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(Tracer.class));
}
@Test
void shouldNotSupplyBeansIfApiIsMissing() {
this.contextRunner.withClassLoader(new FilteredClassLoader("io.opentelemetry.api"))
.run((context) -> assertThat(context).doesNotHaveBean(Tracer.class));
}
@Test
void shouldNotSupplyTracerIfOpenTelemetryIsMissing() {
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(Tracer.class));
}
@Test
void shouldBackOffOnCustomBeans() {
this.contextRunner.withUserConfiguration(OpenTelemetryConfiguration.class, CustomConfiguration.class)
.run((context) -> {
assertThat(context).hasBean("customTracer");
assertThat(context).hasSingleBean(Tracer.class);
});
}
@Configuration(proxyBeanMethods = false)
private static class OpenTelemetryConfiguration {
@Bean
OpenTelemetry openTelemetry() {
return mock(OpenTelemetry.class);
}
}
@Configuration(proxyBeanMethods = false)
private static class CustomConfiguration {
@Bean
Tracer customTracer() {
return mock(Tracer.class);
}
}
}
Loading…
Cancel
Save