Disable propagation of traces if tracing is disabled

Closes gh-38641
This commit is contained in:
Moritz Halbritter
2023-12-05 10:58:04 +01:00
parent a0fc2d48cd
commit 02347abefb
7 changed files with 320 additions and 163 deletions

View File

@@ -24,22 +24,10 @@ import brave.Tracer;
import brave.Tracing;
import brave.Tracing.Builder;
import brave.TracingCustomizer;
import brave.baggage.BaggageField;
import brave.baggage.BaggagePropagation;
import brave.baggage.BaggagePropagation.FactoryBuilder;
import brave.baggage.BaggagePropagationConfig;
import brave.baggage.BaggagePropagationCustomizer;
import brave.baggage.CorrelationScopeConfig.SingleCorrelationField;
import brave.baggage.CorrelationScopeCustomizer;
import brave.baggage.CorrelationScopeDecorator;
import brave.context.slf4j.MDCScopeDecorator;
import brave.handler.SpanHandler;
import brave.propagation.CurrentTraceContext;
import brave.propagation.CurrentTraceContext.ScopeDecorator;
import brave.propagation.CurrentTraceContextCustomizer;
import brave.propagation.Propagation;
import brave.propagation.Propagation.Factory;
import brave.propagation.Propagation.KeyFactory;
import brave.propagation.ThreadLocalCurrentTraceContext;
import brave.sampler.Sampler;
import io.micrometer.tracing.brave.bridge.BraveBaggageManager;
@@ -53,17 +41,15 @@ import io.micrometer.tracing.exporter.SpanFilter;
import io.micrometer.tracing.exporter.SpanReporter;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.tracing.TracingProperties.Baggage.Correlation;
import org.springframework.boot.actuate.autoconfigure.tracing.TracingProperties.Propagation.PropagationType;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.IncompatibleConfigurationException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
@@ -79,9 +65,12 @@ import org.springframework.core.env.Environment;
@AutoConfiguration(before = MicrometerTracingAutoConfiguration.class)
@ConditionalOnClass({ Tracer.class, BraveTracer.class })
@EnableConfigurationProperties(TracingProperties.class)
@Import({ BravePropagationConfigurations.PropagationWithoutBaggage.class,
BravePropagationConfigurations.PropagationWithBaggage.class,
BravePropagationConfigurations.NoPropagation.class })
public class BraveAutoConfiguration {
private static final BraveBaggageManager BRAVE_BAGGAGE_MANAGER = new BraveBaggageManager();
static final BraveBaggageManager BRAVE_BAGGAGE_MANAGER = new BraveBaggageManager();
/**
* Default value for application name if {@code spring.application.name} is not set.
@@ -182,108 +171,4 @@ public class BraveAutoConfiguration {
return new BraveSpanCustomizer(spanCustomizer);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "management.tracing.baggage.enabled", havingValue = "false")
static class BraveNoBaggageConfiguration {
@Bean
@ConditionalOnMissingBean
Factory propagationFactory(TracingProperties properties) {
return CompositePropagationFactory.create(properties.getPropagation());
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "management.tracing.baggage.enabled", matchIfMissing = true)
static class BraveBaggageConfiguration {
private final TracingProperties tracingProperties;
BraveBaggageConfiguration(TracingProperties tracingProperties) {
this.tracingProperties = tracingProperties;
}
@Bean
@ConditionalOnMissingBean
BaggagePropagation.FactoryBuilder propagationFactoryBuilder(
ObjectProvider<BaggagePropagationCustomizer> baggagePropagationCustomizers) {
// There's a chicken-and-egg problem here: to create a builder, we need a
// factory. But the CompositePropagationFactory needs data from the builder.
// We create a throw-away builder with a throw-away factory, and then copy the
// config to the real builder.
FactoryBuilder throwAwayBuilder = BaggagePropagation.newFactoryBuilder(createThrowAwayFactory());
baggagePropagationCustomizers.orderedStream()
.forEach((customizer) -> customizer.customize(throwAwayBuilder));
CompositePropagationFactory propagationFactory = CompositePropagationFactory.create(
this.tracingProperties.getPropagation(), BRAVE_BAGGAGE_MANAGER,
LocalBaggageFields.extractFrom(throwAwayBuilder));
FactoryBuilder builder = BaggagePropagation.newFactoryBuilder(propagationFactory);
throwAwayBuilder.configs().forEach(builder::add);
return builder;
}
@SuppressWarnings("deprecation")
private Factory createThrowAwayFactory() {
return new Factory() {
@Override
public <K> Propagation<K> create(KeyFactory<K> keyFactory) {
return null;
}
};
}
@Bean
@Order(0)
BaggagePropagationCustomizer remoteFieldsBaggagePropagationCustomizer() {
return (builder) -> {
List<String> remoteFields = this.tracingProperties.getBaggage().getRemoteFields();
for (String fieldName : remoteFields) {
builder.add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create(fieldName)));
}
};
}
@Bean
@ConditionalOnMissingBean
Factory propagationFactory(BaggagePropagation.FactoryBuilder factoryBuilder) {
return factoryBuilder.build();
}
@Bean
@ConditionalOnMissingBean
CorrelationScopeDecorator.Builder mdcCorrelationScopeDecoratorBuilder(
ObjectProvider<CorrelationScopeCustomizer> correlationScopeCustomizers) {
CorrelationScopeDecorator.Builder builder = MDCScopeDecorator.newBuilder();
correlationScopeCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder;
}
@Bean
@Order(0)
@ConditionalOnProperty(prefix = "management.tracing.baggage.correlation", name = "enabled",
matchIfMissing = true)
CorrelationScopeCustomizer correlationFieldsCorrelationScopeCustomizer() {
return (builder) -> {
Correlation correlationProperties = this.tracingProperties.getBaggage().getCorrelation();
for (String field : correlationProperties.getFields()) {
BaggageField baggageField = BaggageField.create(field);
SingleCorrelationField correlationField = SingleCorrelationField.newBuilder(baggageField)
.flushOnUpdate()
.build();
builder.add(correlationField);
}
};
}
@Bean
@ConditionalOnMissingBean(CorrelationScopeDecorator.class)
ScopeDecorator correlationScopeDecorator(CorrelationScopeDecorator.Builder builder) {
return builder.build();
}
}
}

View File

@@ -0,0 +1,178 @@
/*
* Copyright 2012-2023 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 brave.baggage.BaggageField;
import brave.baggage.BaggagePropagation;
import brave.baggage.BaggagePropagation.FactoryBuilder;
import brave.baggage.BaggagePropagationConfig;
import brave.baggage.BaggagePropagationCustomizer;
import brave.baggage.CorrelationScopeConfig.SingleCorrelationField;
import brave.baggage.CorrelationScopeCustomizer;
import brave.baggage.CorrelationScopeDecorator;
import brave.context.slf4j.MDCScopeDecorator;
import brave.propagation.CurrentTraceContext.ScopeDecorator;
import brave.propagation.Propagation;
import brave.propagation.Propagation.Factory;
import brave.propagation.Propagation.KeyFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.tracing.TracingProperties.Baggage.Correlation;
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;
import org.springframework.core.annotation.Order;
/**
* Brave propagation configurations. They are imported by {@link BraveAutoConfiguration}.
*
* @author Moritz Halbritter
*/
class BravePropagationConfigurations {
/**
* Propagates traces but no baggage.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "management.tracing.baggage.enabled", havingValue = "false")
static class PropagationWithoutBaggage {
@Bean
@ConditionalOnMissingBean(Factory.class)
@ConditionalOnEnabledTracing
CompositePropagationFactory propagationFactory(TracingProperties properties) {
return CompositePropagationFactory.create(properties.getPropagation());
}
}
/**
* Propagates traces and baggage.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "management.tracing.baggage.enabled", matchIfMissing = true)
@EnableConfigurationProperties(TracingProperties.class)
static class PropagationWithBaggage {
private final TracingProperties tracingProperties;
PropagationWithBaggage(TracingProperties tracingProperties) {
this.tracingProperties = tracingProperties;
}
@Bean
@ConditionalOnMissingBean
BaggagePropagation.FactoryBuilder propagationFactoryBuilder(
ObjectProvider<BaggagePropagationCustomizer> baggagePropagationCustomizers) {
// There's a chicken-and-egg problem here: to create a builder, we need a
// factory. But the CompositePropagationFactory needs data from the builder.
// We create a throw-away builder with a throw-away factory, and then copy the
// config to the real builder.
FactoryBuilder throwAwayBuilder = BaggagePropagation.newFactoryBuilder(createThrowAwayFactory());
baggagePropagationCustomizers.orderedStream()
.forEach((customizer) -> customizer.customize(throwAwayBuilder));
CompositePropagationFactory propagationFactory = CompositePropagationFactory.create(
this.tracingProperties.getPropagation(), BraveAutoConfiguration.BRAVE_BAGGAGE_MANAGER,
LocalBaggageFields.extractFrom(throwAwayBuilder));
FactoryBuilder builder = BaggagePropagation.newFactoryBuilder(propagationFactory);
throwAwayBuilder.configs().forEach(builder::add);
return builder;
}
@SuppressWarnings("deprecation")
private Factory createThrowAwayFactory() {
return new Factory() {
@Override
public <K> Propagation<K> create(KeyFactory<K> keyFactory) {
return null;
}
};
}
@Bean
@Order(0)
BaggagePropagationCustomizer remoteFieldsBaggagePropagationCustomizer() {
return (builder) -> {
List<String> remoteFields = this.tracingProperties.getBaggage().getRemoteFields();
for (String fieldName : remoteFields) {
builder.add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create(fieldName)));
}
};
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledTracing
Factory propagationFactory(BaggagePropagation.FactoryBuilder factoryBuilder) {
return factoryBuilder.build();
}
@Bean
@ConditionalOnMissingBean
CorrelationScopeDecorator.Builder mdcCorrelationScopeDecoratorBuilder(
ObjectProvider<CorrelationScopeCustomizer> correlationScopeCustomizers) {
CorrelationScopeDecorator.Builder builder = MDCScopeDecorator.newBuilder();
correlationScopeCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder;
}
@Bean
@Order(0)
@ConditionalOnProperty(prefix = "management.tracing.baggage.correlation", name = "enabled",
matchIfMissing = true)
CorrelationScopeCustomizer correlationFieldsCorrelationScopeCustomizer() {
return (builder) -> {
Correlation correlationProperties = this.tracingProperties.getBaggage().getCorrelation();
for (String field : correlationProperties.getFields()) {
BaggageField baggageField = BaggageField.create(field);
SingleCorrelationField correlationField = SingleCorrelationField.newBuilder(baggageField)
.flushOnUpdate()
.build();
builder.add(correlationField);
}
};
}
@Bean
@ConditionalOnMissingBean(CorrelationScopeDecorator.class)
ScopeDecorator correlationScopeDecorator(CorrelationScopeDecorator.Builder builder) {
return builder.build();
}
}
/**
* Propagates neither traces nor baggage.
*/
@Configuration(proxyBeanMethods = false)
static class NoPropagation {
@Bean
@ConditionalOnMissingBean(Factory.class)
CompositePropagationFactory noopPropagationFactory() {
return CompositePropagationFactory.noop();
}
}
}

View File

@@ -17,6 +17,7 @@
package org.springframework.boot.actuate.autoconfigure.tracing;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -84,6 +85,14 @@ class CompositePropagationFactory extends Propagation.Factory {
.orElse(context);
}
/**
* Creates a new {@link CompositePropagationFactory} which doesn't do any propagation.
* @return the {@link CompositePropagationFactory}
*/
static CompositePropagationFactory noop() {
return new CompositePropagationFactory(Collections.emptyList(), Collections.emptyList());
}
/**
* Creates a new {@link CompositePropagationFactory}.
* @param properties the propagation properties

View File

@@ -32,9 +32,7 @@ import io.micrometer.tracing.otel.bridge.OtelPropagator;
import io.micrometer.tracing.otel.bridge.OtelSpanCustomizer;
import io.micrometer.tracing.otel.bridge.OtelTracer;
import io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher;
import io.micrometer.tracing.otel.bridge.Slf4JBaggageEventListener;
import io.micrometer.tracing.otel.bridge.Slf4JEventListener;
import io.micrometer.tracing.otel.propagation.BaggageTextMapPropagator;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.api.trace.Tracer;
@@ -56,10 +54,9 @@ import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
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;
import org.springframework.context.annotation.Import;
/**
* {@link EnableAutoConfiguration Auto-configuration} for OpenTelemetry tracing.
@@ -72,6 +69,9 @@ import org.springframework.context.annotation.Configuration;
@AutoConfiguration(value = "openTelemetryTracingAutoConfiguration", before = MicrometerTracingAutoConfiguration.class)
@ConditionalOnClass({ OtelTracer.class, SdkTracerProvider.class, OpenTelemetry.class })
@EnableConfigurationProperties(TracingProperties.class)
@Import({ OpenTelemetryPropagationConfigurations.PropagationWithoutBaggage.class,
OpenTelemetryPropagationConfigurations.PropagationWithBaggage.class,
OpenTelemetryPropagationConfigurations.NoPropagation.class })
public class OpenTelemetryAutoConfiguration {
private final TracingProperties tracingProperties;
@@ -172,45 +172,6 @@ public class OpenTelemetryAutoConfiguration {
return new OtelSpanCustomizer();
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "management.tracing.baggage", name = "enabled", matchIfMissing = true)
static class BaggageConfiguration {
private final TracingProperties tracingProperties;
BaggageConfiguration(TracingProperties tracingProperties) {
this.tracingProperties = tracingProperties;
}
@Bean
TextMapPropagator textMapPropagatorWithBaggage(OtelCurrentTraceContext otelCurrentTraceContext) {
List<String> remoteFields = this.tracingProperties.getBaggage().getRemoteFields();
BaggageTextMapPropagator baggagePropagator = new BaggageTextMapPropagator(remoteFields,
new OtelBaggageManager(otelCurrentTraceContext, remoteFields, Collections.emptyList()));
return CompositeTextMapPropagator.create(this.tracingProperties.getPropagation(), baggagePropagator);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "management.tracing.baggage.correlation", name = "enabled",
matchIfMissing = true)
Slf4JBaggageEventListener otelSlf4JBaggageEventListener() {
return new Slf4JBaggageEventListener(this.tracingProperties.getBaggage().getCorrelation().getFields());
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "management.tracing.baggage", name = "enabled", havingValue = "false")
static class NoBaggageConfiguration {
@Bean
TextMapPropagator textMapPropagator(TracingProperties properties) {
return CompositeTextMapPropagator.create(properties.getPropagation(), null);
}
}
static class OTelEventPublisher implements EventPublisher {
private final List<EventListener> listeners;

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2012-2023 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.Collections;
import java.util.List;
import io.micrometer.tracing.otel.bridge.OtelBaggageManager;
import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext;
import io.micrometer.tracing.otel.bridge.Slf4JBaggageEventListener;
import io.micrometer.tracing.otel.propagation.BaggageTextMapPropagator;
import io.opentelemetry.context.propagation.TextMapPropagator;
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;
/**
* OpenTelemetry propagation configurations. They are imported by
* {@link OpenTelemetryAutoConfiguration}.
*
* @author Moritz Halbritter
*/
class OpenTelemetryPropagationConfigurations {
/**
* Propagates traces but no baggage.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "management.tracing.baggage", name = "enabled", havingValue = "false")
@EnableConfigurationProperties(TracingProperties.class)
static class PropagationWithoutBaggage {
@Bean
@ConditionalOnEnabledTracing
TextMapPropagator textMapPropagator(TracingProperties properties) {
return CompositeTextMapPropagator.create(properties.getPropagation(), null);
}
}
/**
* Propagates traces and baggage.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "management.tracing.baggage", name = "enabled", matchIfMissing = true)
@EnableConfigurationProperties(TracingProperties.class)
static class PropagationWithBaggage {
private final TracingProperties tracingProperties;
PropagationWithBaggage(TracingProperties tracingProperties) {
this.tracingProperties = tracingProperties;
}
@Bean
@ConditionalOnEnabledTracing
TextMapPropagator textMapPropagatorWithBaggage(OtelCurrentTraceContext otelCurrentTraceContext) {
List<String> remoteFields = this.tracingProperties.getBaggage().getRemoteFields();
BaggageTextMapPropagator baggagePropagator = new BaggageTextMapPropagator(remoteFields,
new OtelBaggageManager(otelCurrentTraceContext, remoteFields, Collections.emptyList()));
return CompositeTextMapPropagator.create(this.tracingProperties.getPropagation(), baggagePropagator);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "management.tracing.baggage.correlation", name = "enabled",
matchIfMissing = true)
Slf4JBaggageEventListener otelSlf4JBaggageEventListener() {
return new Slf4JBaggageEventListener(this.tracingProperties.getBaggage().getCorrelation().getFields());
}
}
/**
* Propagates neither traces nor baggage.
*/
@Configuration(proxyBeanMethods = false)
static class NoPropagation {
@Bean
@ConditionalOnMissingBean
TextMapPropagator noopTextMapPropagator() {
return TextMapPropagator.noop();
}
}
}

View File

@@ -344,6 +344,16 @@ class BraveAutoConfigurationTests {
});
}
@Test
void shouldDisablePropagationIfTracingIsDisabled() {
this.contextRunner.withPropertyValues("management.tracing.enabled=false").run((context) -> {
assertThat(context).hasSingleBean(Factory.class);
Factory factory = context.getBean(Factory.class);
Propagation<String> propagation = factory.get();
assertThat(propagation.keys()).isEmpty();
});
}
private void injectToMap(Map<String, String> map, String key, String value) {
map.put(key, value);
}

View File

@@ -290,6 +290,15 @@ class OpenTelemetryAutoConfigurationTests {
});
}
@Test
void shouldDisablePropagationIfTracingIsDisabled() {
this.contextRunner.withPropertyValues("management.tracing.enabled=false").run((context) -> {
assertThat(context).hasSingleBean(TextMapPropagator.class);
TextMapPropagator propagator = context.getBean(TextMapPropagator.class);
assertThat(propagator.fields()).isEmpty();
});
}
private List<TextMapPropagator> getInjectors(TextMapPropagator propagator) {
assertThat(propagator).as("propagator").isNotNull();
if (propagator instanceof CompositeTextMapPropagator compositePropagator) {