Require micrometer-tracing-bridge-otel to auto-configure OTel
Closes gh-32503pull/32525/head
parent
6cc3619675
commit
5449397463
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
111
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryConfigurationsMicrometerConfigurationTests.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfigurationTests.java
111
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryConfigurationsMicrometerConfigurationTests.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfigurationTests.java
@ -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…
Reference in New Issue