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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
89
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
89
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