mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-17 12:09:16 +00:00
Add baggage tag fields to spans with OpenTelemetry
With OpenTelemetry, fields listed in management.tracing.baggage.tag-fields only became span tags when the application itself touched the baggage through the Tracer API. Baggage that arrived with a request was propagated correctly, but spans were not tagged with it (see micrometer-metrics/tracing#933). This commit registers Micrometer Tracing's BaggageTaggingSpanProcessor whenever baggage is enabled and at least one tag field is configured, so that spans are tagged with the baggage that is present in their parent context. A custom BaggageTaggingSpanProcessor bean backs off the auto-configured one. The tag-fields property is now also mentioned in the baggage section of the tracing documentation. See gh-51656 Signed-off-by: Oleksandr Shevchenko <oleksandr.shevchenko@datarobot.com>
This commit is contained in:
committed by
Andy Wilkinson
parent
924d10733e
commit
ce75f008b0
+25
@@ -18,18 +18,22 @@ package org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.micrometer.tracing.otel.bridge.BaggageTaggingSpanProcessor;
|
||||
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.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.OnPropertyListCondition;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.micrometer.tracing.autoconfigure.ConditionalOnEnabledTracingExport;
|
||||
import org.springframework.boot.micrometer.tracing.autoconfigure.TracingProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
@@ -37,6 +41,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
* {@link OpenTelemetryTracingAutoConfiguration}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Oleksandr Shevchenko
|
||||
*/
|
||||
class OpenTelemetryPropagationConfigurations {
|
||||
|
||||
@@ -87,6 +92,26 @@ class OpenTelemetryPropagationConfigurations {
|
||||
return new Slf4JBaggageEventListener(this.tracingProperties.getBaggage().getCorrelation().getFields());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@Conditional(OnBaggageTagFieldsCondition.class)
|
||||
BaggageTaggingSpanProcessor otelBaggageTaggingSpanProcessor() {
|
||||
return new BaggageTaggingSpanProcessor(this.tracingProperties.getBaggage().getTagFields());
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition that matches when {@code management.tracing.baggage.tag-fields} has
|
||||
* at least one entry.
|
||||
*/
|
||||
static class OnBaggageTagFieldsCondition extends OnPropertyListCondition {
|
||||
|
||||
OnBaggageTagFieldsCondition() {
|
||||
super("management.tracing.baggage.tag-fields",
|
||||
() -> ConditionMessage.forCondition("Baggage tag fields"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+63
@@ -30,6 +30,7 @@ import io.micrometer.tracing.SpanCustomizer;
|
||||
import io.micrometer.tracing.Tracer.SpanInScope;
|
||||
import io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler;
|
||||
import io.micrometer.tracing.handler.PropagatingSenderTracingObservationHandler;
|
||||
import io.micrometer.tracing.otel.bridge.BaggageTaggingSpanProcessor;
|
||||
import io.micrometer.tracing.otel.bridge.EventListener;
|
||||
import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext;
|
||||
import io.micrometer.tracing.otel.bridge.OtelPropagator;
|
||||
@@ -40,18 +41,21 @@ import io.micrometer.tracing.otel.bridge.Slf4JBaggageEventListener;
|
||||
import io.micrometer.tracing.otel.bridge.Slf4JEventListener;
|
||||
import io.micrometer.tracing.otel.propagation.BaggageTextMapPropagator;
|
||||
import io.micrometer.tracing.propagation.Propagator;
|
||||
import io.opentelemetry.api.baggage.Baggage;
|
||||
import io.opentelemetry.api.common.AttributeKey;
|
||||
import io.opentelemetry.api.common.Attributes;
|
||||
import io.opentelemetry.api.metrics.MeterProvider;
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.api.trace.Tracer;
|
||||
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
|
||||
import io.opentelemetry.context.Scope;
|
||||
import io.opentelemetry.context.propagation.ContextPropagators;
|
||||
import io.opentelemetry.context.propagation.TextMapPropagator;
|
||||
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
|
||||
import io.opentelemetry.extension.trace.propagation.B3Propagator;
|
||||
import io.opentelemetry.sdk.common.CompletableResultCode;
|
||||
import io.opentelemetry.sdk.resources.Resource;
|
||||
import io.opentelemetry.sdk.trace.ReadableSpan;
|
||||
import io.opentelemetry.sdk.trace.SdkTracerProvider;
|
||||
import io.opentelemetry.sdk.trace.SpanLimits;
|
||||
import io.opentelemetry.sdk.trace.SpanProcessor;
|
||||
@@ -91,6 +95,7 @@ import static org.mockito.Mockito.mock;
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Yanming Zhou
|
||||
* @author Oleksandr Shevchenko
|
||||
*/
|
||||
class OpenTelemetryTracingAutoConfigurationTests {
|
||||
|
||||
@@ -390,6 +395,54 @@ class OpenTelemetryTracingAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupplyBaggageTaggingSpanProcessorWhenTagFieldsAreConfigured() {
|
||||
this.contextRunner.withPropertyValues("management.tracing.baggage.tag-fields=t1,t2").run((context) -> {
|
||||
assertThat(context).hasSingleBean(BaggageTaggingSpanProcessor.class);
|
||||
SpanProcessors spanProcessors = context.getBean(SpanProcessors.class);
|
||||
assertThat(spanProcessors).anyMatch(BaggageTaggingSpanProcessor.class::isInstance);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotSupplyBaggageTaggingSpanProcessorWhenTagFieldsAreNotConfigured() {
|
||||
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(BaggageTaggingSpanProcessor.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotSupplyBaggageTaggingSpanProcessorWhenBaggageDisabled() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("management.tracing.baggage.enabled=false", "management.tracing.baggage.tag-fields=t1")
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(BaggageTaggingSpanProcessor.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBackOffOnCustomBaggageTaggingSpanProcessor() {
|
||||
this.contextRunner.withPropertyValues("management.tracing.baggage.tag-fields=t1")
|
||||
.withUserConfiguration(CustomBaggageTaggingSpanProcessorConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasBean("customBaggageTaggingSpanProcessor");
|
||||
assertThat(context).hasSingleBean(BaggageTaggingSpanProcessor.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void baggageTaggingSpanProcessorShouldTagSpansWithBaggageFromParentContext() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("management.tracing.baggage.tag-fields=t1",
|
||||
"management.tracing.sampling.probability=1.0")
|
||||
.run((context) -> {
|
||||
Tracer tracer = context.getBean(Tracer.class);
|
||||
try (Scope scope = Baggage.builder().put("t1", "v1").put("other", "v2").build().makeCurrent()) {
|
||||
Span span = tracer.spanBuilder("test").startSpan();
|
||||
span.end();
|
||||
assertThat(span).isInstanceOf(ReadableSpan.class);
|
||||
assertThat(((ReadableSpan) span).getAttribute(AttributeKey.stringKey("t1"))).isEqualTo("v1");
|
||||
assertThat(((ReadableSpan) span).getAttribute(AttributeKey.stringKey("other"))).isNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void spanLimitsShouldBeConfiguredWithCustomProperties() {
|
||||
this.contextRunner
|
||||
@@ -608,6 +661,16 @@ class OpenTelemetryTracingAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
private static final class CustomBaggageTaggingSpanProcessorConfiguration {
|
||||
|
||||
@Bean
|
||||
BaggageTaggingSpanProcessor customBaggageTaggingSpanProcessor() {
|
||||
return new BaggageTaggingSpanProcessor(List.of("custom"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
private static final class AdditionalSpanProcessorConfiguration {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user