Springboot propagators (#3506)
* set propagators in sdk builder * add test for propagators * introduce CompositeTextMapPropagator * simplify composite TextMapPropagator creation, use SDK names for PropagationType * Update instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/propagators/PropagationAutoConfiguration.java Co-authored-by: Mateusz Rzeszutek <mrzeszutek@splunk.com> * Update instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/propagators/PropagationAutoConfigurationTest.java Co-authored-by: Mateusz Rzeszutek <mrzeszutek@splunk.com> * replace enum with string, remove unnecessary noop propagator addition * add warning and test for unsupported value Co-authored-by: Mateusz Rzeszutek <mrzeszutek@splunk.com>
This commit is contained in:
parent
ac85c3cbd8
commit
e46ae82e94
|
@ -21,6 +21,8 @@ dependencies {
|
|||
compileOnly("org.springframework.boot:spring-boot-starter-webflux:${versions["org.springframework.boot"]}")
|
||||
|
||||
compileOnly("io.opentelemetry:opentelemetry-extension-annotations")
|
||||
compileOnly("io.opentelemetry:opentelemetry-extension-trace-propagators")
|
||||
compileOnly("io.opentelemetry:opentelemetry-extension-aws")
|
||||
compileOnly("io.opentelemetry:opentelemetry-exporter-logging")
|
||||
compileOnly("io.opentelemetry:opentelemetry-exporter-jaeger")
|
||||
compileOnly("io.opentelemetry:opentelemetry-exporter-otlp")
|
||||
|
@ -39,6 +41,8 @@ dependencies {
|
|||
testImplementation("io.opentelemetry:opentelemetry-sdk")
|
||||
testImplementation("io.opentelemetry:opentelemetry-sdk-testing")
|
||||
testImplementation("io.opentelemetry:opentelemetry-extension-annotations")
|
||||
testImplementation("io.opentelemetry:opentelemetry-extension-trace-propagators")
|
||||
testImplementation("io.opentelemetry:opentelemetry-extension-aws")
|
||||
testImplementation("io.opentelemetry:opentelemetry-exporter-logging")
|
||||
testImplementation("io.opentelemetry:opentelemetry-exporter-jaeger")
|
||||
testImplementation("io.opentelemetry:opentelemetry-exporter-otlp")
|
||||
|
|
|
@ -7,6 +7,7 @@ package io.opentelemetry.instrumentation.spring.autoconfigure;
|
|||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.api.trace.TracerProvider;
|
||||
import io.opentelemetry.context.propagation.ContextPropagators;
|
||||
import io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||
import io.opentelemetry.sdk.trace.SdkTracerProvider;
|
||||
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
|
||||
|
@ -36,6 +37,7 @@ public class OpenTelemetryAutoConfiguration {
|
|||
@ConditionalOnMissingBean
|
||||
public OpenTelemetry openTelemetry(
|
||||
SamplerProperties samplerProperties,
|
||||
ObjectProvider<ContextPropagators> propagatorsProvider,
|
||||
ObjectProvider<List<SpanExporter>> spanExportersProvider) {
|
||||
SdkTracerProviderBuilder tracerProviderBuilder = SdkTracerProvider.builder();
|
||||
|
||||
|
@ -48,6 +50,12 @@ public class OpenTelemetryAutoConfiguration {
|
|||
tracerProviderBuilder
|
||||
.setSampler(Sampler.traceIdRatioBased(samplerProperties.getProbability()))
|
||||
.build();
|
||||
return OpenTelemetrySdk.builder().setTracerProvider(tracerProvider).buildAndRegisterGlobal();
|
||||
|
||||
ContextPropagators propagators = propagatorsProvider.getIfAvailable(ContextPropagators::noop);
|
||||
|
||||
return OpenTelemetrySdk.builder()
|
||||
.setTracerProvider(tracerProvider)
|
||||
.setPropagators(propagators)
|
||||
.buildAndRegisterGlobal();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.autoconfigure.propagators;
|
||||
|
||||
import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator;
|
||||
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
|
||||
import io.opentelemetry.context.propagation.TextMapPropagator;
|
||||
import io.opentelemetry.extension.aws.AwsXrayPropagator;
|
||||
import io.opentelemetry.extension.trace.propagation.B3Propagator;
|
||||
import io.opentelemetry.extension.trace.propagation.JaegerPropagator;
|
||||
import io.opentelemetry.extension.trace.propagation.OtTracePropagator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/** Factory of composite {@link TextMapPropagator}. Defaults to W3C and BAGGAGE. */
|
||||
public final class CompositeTextMapPropagatorFactory {
|
||||
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(CompositeTextMapPropagatorFactory.class);
|
||||
|
||||
static TextMapPropagator getCompositeTextMapPropagator(
|
||||
BeanFactory beanFactory, List<String> types) {
|
||||
|
||||
Set<TextMapPropagator> propagators = new HashSet<>();
|
||||
|
||||
for (String type : types) {
|
||||
switch (type) {
|
||||
case "b3":
|
||||
if (isOnClasspath("io.opentelemetry.extension.trace.propagation.B3Propagator")) {
|
||||
propagators.add(
|
||||
beanFactory
|
||||
.getBeanProvider(B3Propagator.class)
|
||||
.getIfAvailable(B3Propagator::injectingSingleHeader));
|
||||
}
|
||||
break;
|
||||
case "b3multi":
|
||||
if (isOnClasspath("io.opentelemetry.extension.trace.propagation.B3Propagator")) {
|
||||
propagators.add(
|
||||
beanFactory
|
||||
.getBeanProvider(B3Propagator.class)
|
||||
.getIfAvailable(B3Propagator::injectingMultiHeaders));
|
||||
}
|
||||
break;
|
||||
case "jaeger":
|
||||
if (isOnClasspath("io.opentelemetry.extension.trace.propagation.JaegerPropagator")) {
|
||||
propagators.add(
|
||||
beanFactory
|
||||
.getBeanProvider(JaegerPropagator.class)
|
||||
.getIfAvailable(JaegerPropagator::getInstance));
|
||||
}
|
||||
break;
|
||||
case "ottrace":
|
||||
if (isOnClasspath("io.opentelemetry.extension.trace.propagation.OtTracerPropagator")) {
|
||||
propagators.add(
|
||||
beanFactory
|
||||
.getBeanProvider(OtTracePropagator.class)
|
||||
.getIfAvailable(OtTracePropagator::getInstance));
|
||||
}
|
||||
break;
|
||||
case "xray":
|
||||
if (isOnClasspath("io.opentelemetry.extension.aws.AwsXrayPropagator")) {
|
||||
propagators.add(
|
||||
beanFactory
|
||||
.getBeanProvider(AwsXrayPropagator.class)
|
||||
.getIfAvailable(AwsXrayPropagator::getInstance));
|
||||
}
|
||||
break;
|
||||
case "tracecontext":
|
||||
propagators.add(W3CTraceContextPropagator.getInstance());
|
||||
break;
|
||||
case "baggage":
|
||||
propagators.add(W3CBaggagePropagator.getInstance());
|
||||
break;
|
||||
default:
|
||||
logger.warn("Unsupported type of propagator: {}", type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return TextMapPropagator.composite(propagators);
|
||||
}
|
||||
|
||||
private static boolean isOnClasspath(String clazz) {
|
||||
return ClassUtils.isPresent(clazz, null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.autoconfigure.propagators;
|
||||
|
||||
import io.opentelemetry.context.propagation.ContextPropagators;
|
||||
import io.opentelemetry.context.propagation.TextMapPropagator;
|
||||
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/** Configures {@link ContextPropagators} bean for propagation. */
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(PropagationProperties.class)
|
||||
@AutoConfigureBefore(OpenTelemetryAutoConfiguration.class)
|
||||
@ConditionalOnProperty(prefix = "otel.propagation", name = "enabled", matchIfMissing = true)
|
||||
public class PropagationAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
ContextPropagators contextPropagators(ObjectProvider<List<TextMapPropagator>> propagators) {
|
||||
List<TextMapPropagator> mapPropagators = propagators.getIfAvailable(Collections::emptyList);
|
||||
if (mapPropagators.isEmpty()) {
|
||||
return ContextPropagators.noop();
|
||||
}
|
||||
return ContextPropagators.create(TextMapPropagator.composite(mapPropagators));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class PropagatorsConfiguration {
|
||||
|
||||
@Bean
|
||||
TextMapPropagator compositeTextMapPropagator(
|
||||
BeanFactory beanFactory, PropagationProperties properties) {
|
||||
return CompositeTextMapPropagatorFactory.getCompositeTextMapPropagator(
|
||||
beanFactory, properties.getType());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.autoconfigure.propagators;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/** Configuration for propagators. */
|
||||
@ConfigurationProperties(prefix = "otel.propagation")
|
||||
public final class PropagationProperties {
|
||||
|
||||
private List<String> type = Arrays.asList("tracecontext", "baggage");
|
||||
|
||||
public List<String> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(List<String> type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ io.opentelemetry.instrumentation.spring.autoconfigure.exporters.jaeger.JaegerSpa
|
|||
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpGrpcSpanExporterAutoConfiguration,\
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.zipkin.ZipkinSpanExporterAutoConfiguration,\
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging.LoggingSpanExporterAutoConfiguration,\
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.propagators.PropagationAutoConfiguration,\
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration,\
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.httpclients.resttemplate.RestTemplateAutoConfiguration,\
|
||||
io.opentelemetry.instrumentation.spring.autoconfigure.httpclients.webclient.WebClientAutoConfiguration,\
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.autoconfigure.propagators;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||
import io.opentelemetry.context.propagation.TextMapPropagator;
|
||||
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
|
||||
class PropagationAutoConfigurationTest {
|
||||
|
||||
private final ApplicationContextRunner contextRunner =
|
||||
new ApplicationContextRunner()
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(
|
||||
OpenTelemetryAutoConfiguration.class, PropagationAutoConfiguration.class));
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
GlobalOpenTelemetry.resetForTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("when propagation is ENABLED should initialize PropagationAutoConfiguration bean")
|
||||
void shouldBeConfigured() {
|
||||
|
||||
this.contextRunner
|
||||
.withPropertyValues("otel.propagation.enabled=true")
|
||||
.run(context -> assertThat(context.containsBean("propagationAutoConfiguration")).isTrue());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName(
|
||||
"when propagation is DISABLED should NOT initialize PropagationAutoConfiguration bean")
|
||||
void shouldNotBeConfigured() {
|
||||
|
||||
this.contextRunner
|
||||
.withPropertyValues("otel.propagation.enabled=false")
|
||||
.run(context -> assertThat(context.containsBean("propagationAutoConfiguration")).isFalse());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName(
|
||||
"when propagation enabled property is MISSING should initialize PropagationAutoConfiguration bean")
|
||||
void noProperty() {
|
||||
this.contextRunner.run(
|
||||
context -> assertThat(context.containsBean("propagationAutoConfiguration")).isTrue());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("when no propagators are defined should contain default propagators")
|
||||
void shouldContainDefaults() {
|
||||
|
||||
this.contextRunner.run(
|
||||
context ->
|
||||
assertThat(
|
||||
context.getBean("compositeTextMapPropagator", TextMapPropagator.class).fields())
|
||||
.contains("traceparent", "baggage"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("when propagation is set to b3 should contain only b3 propagator")
|
||||
void shouldContainB3() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("otel.propagation.type=b3")
|
||||
.run(
|
||||
context -> {
|
||||
TextMapPropagator compositePropagator =
|
||||
context.getBean("compositeTextMapPropagator", TextMapPropagator.class);
|
||||
|
||||
assertThat(compositePropagator.fields())
|
||||
.contains("b3")
|
||||
.doesNotContain("baggage", "traceparent");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("when propagation is set to unsupported value should create an empty propagator")
|
||||
void shouldCreateNoop() {
|
||||
|
||||
this.contextRunner
|
||||
.withPropertyValues("otel.propagation.type=invalid")
|
||||
.run(
|
||||
context -> {
|
||||
TextMapPropagator compositePropagator =
|
||||
context.getBean("compositeTextMapPropagator", TextMapPropagator.class);
|
||||
|
||||
assertThat(compositePropagator.fields()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("when propagation is set to some values should contain only supported values")
|
||||
void shouldContainOnlySupported() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("otel.propagation.type=invalid,b3")
|
||||
.run(
|
||||
context -> {
|
||||
TextMapPropagator compositePropagator =
|
||||
context.getBean("compositeTextMapPropagator", TextMapPropagator.class);
|
||||
|
||||
assertThat(compositePropagator.fields()).containsExactly("b3");
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.autoconfigure.propagators;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
|
||||
import java.util.Arrays;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
|
||||
public class PropagationPropertiesTest {
|
||||
|
||||
private final ApplicationContextRunner contextRunner =
|
||||
new ApplicationContextRunner()
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(
|
||||
OpenTelemetryAutoConfiguration.class, PropagationAutoConfiguration.class));
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
GlobalOpenTelemetry.resetForTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("when propagation is SET should set PropagationProperties with given propagators")
|
||||
void hasType() {
|
||||
|
||||
this.contextRunner
|
||||
.withPropertyValues("otel.propagation.type=xray,b3")
|
||||
.run(
|
||||
context -> {
|
||||
PropagationProperties propertiesBean = context.getBean(PropagationProperties.class);
|
||||
|
||||
assertThat(propertiesBean.getType()).isEqualTo(Arrays.asList("xray", "b3"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("when propagation is DEFAULT should set PropagationProperties to default values")
|
||||
void hasDefaultTypes() {
|
||||
|
||||
this.contextRunner.run(
|
||||
context ->
|
||||
assertThat(context.getBean(PropagationProperties.class).getType())
|
||||
.containsExactly("tracecontext", "baggage"));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue