diff --git a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/tracing.adoc b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/tracing.adoc index 83bdcdb36f5..de30d41e9ef 100644 --- a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/tracing.adoc +++ b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/tracing.adoc @@ -150,6 +150,39 @@ Use the `management.tracing.export.zipkin.*` configuration properties to configu +[[actuator.micrometer-tracing.sampling]] +== Sampling + +By default, Spring Boot samples only 10% of requests to prevent overwhelming the trace backend. +The configprop:management.tracing.sampling.probability[] property can be used to configure this. + +When using OpenTelemetry, you can also configure which sampler is used via the configprop:management.opentelemetry.tracing.sampler[] property. +The following samplers are supported: + +|=== +|Sampler |Description + +|`always-on` +|Samples every trace. + +|`always-off` +|Discards every trace. + +|`trace-id-ratio` +|Samples a fraction of traces based on configprop:management.tracing.sampling.probability[]. + +|`parent-based-always-on` +|If the parent span is sampled, samples the child span. If there is no parent, samples every trace. + +|`parent-based-always-off` +|If the parent span is sampled, samples the child span. If there is no parent, discards every trace. + +|`parent-based-trace-id-ratio` (default) +|If the parent span is sampled, samples the child span. If there is no parent, samples a fraction of traces based on configprop:management.tracing.sampling.probability[]. +|=== + + + [[actuator.micrometer-tracing.micrometer-observation]] == Integration with Micrometer Observation diff --git a/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfiguration.java b/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfiguration.java index 8b2b2322964..f2d18faa818 100644 --- a/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfiguration.java +++ b/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfiguration.java @@ -58,6 +58,9 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.micrometer.tracing.autoconfigure.MicrometerTracingAutoConfiguration; import org.springframework.boot.micrometer.tracing.autoconfigure.NoopTracerAutoConfiguration; import org.springframework.boot.micrometer.tracing.autoconfigure.TracingProperties; +import org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure.OpenTelemetryPropagationConfigurations.NoPropagation; +import org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure.OpenTelemetryPropagationConfigurations.PropagationWithBaggage; +import org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure.OpenTelemetryPropagationConfigurations.PropagationWithoutBaggage; import org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure.OpenTelemetryTracingProperties.Export; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; @@ -74,9 +77,7 @@ import org.springframework.util.CollectionUtils; @AutoConfiguration(before = { MicrometerTracingAutoConfiguration.class, NoopTracerAutoConfiguration.class }) @ConditionalOnClass({ OtelTracer.class, SdkTracerProvider.class, OpenTelemetry.class }) @EnableConfigurationProperties({ TracingProperties.class, OpenTelemetryTracingProperties.class }) -@Import({ OpenTelemetryPropagationConfigurations.PropagationWithoutBaggage.class, - OpenTelemetryPropagationConfigurations.PropagationWithBaggage.class, - OpenTelemetryPropagationConfigurations.NoPropagation.class }) +@Import({ PropagationWithoutBaggage.class, PropagationWithBaggage.class, NoPropagation.class }) public final class OpenTelemetryTracingAutoConfiguration { private static final Log logger = LogFactory.getLog(OpenTelemetryTracingAutoConfiguration.class); @@ -113,8 +114,15 @@ public final class OpenTelemetryTracingAutoConfiguration { @Bean @ConditionalOnMissingBean Sampler otelSampler() { - Sampler rootSampler = Sampler.traceIdRatioBased(this.tracingProperties.getSampling().getProbability()); - return Sampler.parentBased(rootSampler); + return switch (this.openTelemetryTracingProperties.getSampler()) { + case ALWAYS_ON -> Sampler.alwaysOn(); + case ALWAYS_OFF -> Sampler.alwaysOff(); + case TRACE_ID_RATIO -> Sampler.traceIdRatioBased(this.tracingProperties.getSampling().getProbability()); + case PARENT_BASED_ALWAYS_ON -> Sampler.parentBased(Sampler.alwaysOn()); + case PARENT_BASED_ALWAYS_OFF -> Sampler.parentBased(Sampler.alwaysOff()); + case PARENT_BASED_TRACE_ID_RATIO -> + Sampler.parentBased(Sampler.traceIdRatioBased(this.tracingProperties.getSampling().getProbability())); + }; } @Bean diff --git a/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingProperties.java b/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingProperties.java index ecf80b850ef..ffeeeb15f28 100644 --- a/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingProperties.java +++ b/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingProperties.java @@ -34,10 +34,23 @@ public class OpenTelemetryTracingProperties { */ private final Export export = new Export(); + /** + * Sampler to use. + */ + private Sampler sampler = Sampler.PARENT_BASED_TRACE_ID_RATIO; + public Export getExport() { return this.export; } + public Sampler getSampler() { + return this.sampler; + } + + public void setSampler(Sampler sampler) { + this.sampler = sampler; + } + public static class Export { /** @@ -108,4 +121,14 @@ public class OpenTelemetryTracingProperties { } + /** + * Supported samplers. + */ + public enum Sampler { + + ALWAYS_ON, ALWAYS_OFF, TRACE_ID_RATIO, PARENT_BASED_ALWAYS_ON, PARENT_BASED_ALWAYS_OFF, + PARENT_BASED_TRACE_ID_RATIO + + } + } diff --git a/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java b/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java index c09a8ca3156..762aa0eaec3 100644 --- a/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java +++ b/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java @@ -118,14 +118,77 @@ class OpenTelemetryTracingAutoConfigurationTests { } @Test - void samplerIsParentBased() { + void samplerIsParentBasedTraceIdRatioByDefault() { this.contextRunner.run((context) -> { Sampler sampler = context.getBean(Sampler.class); - assertThat(sampler).isNotNull(); - assertThat(sampler.getDescription()).startsWith("ParentBased{"); + assertThat(sampler.getDescription()).startsWith("ParentBased{root:TraceIdRatioBased{"); }); } + @Test + void samplerUsesCustomProbability() { + this.contextRunner.withPropertyValues("management.tracing.sampling.probability=0.5").run((context) -> { + Sampler sampler = context.getBean(Sampler.class); + assertThat(sampler.getDescription()).contains("0.5"); + }); + } + + @Test + void samplerCanBeSetToAlwaysOn() { + this.contextRunner.withPropertyValues("management.opentelemetry.tracing.sampler=always-on").run((context) -> { + Sampler sampler = context.getBean(Sampler.class); + assertThat(sampler.getDescription()).isEqualTo("AlwaysOnSampler"); + }); + } + + @Test + void samplerCanBeSetToAlwaysOff() { + this.contextRunner.withPropertyValues("management.opentelemetry.tracing.sampler=always-off").run((context) -> { + Sampler sampler = context.getBean(Sampler.class); + assertThat(sampler.getDescription()).isEqualTo("AlwaysOffSampler"); + }); + } + + @Test + void samplerCanBeSetToTraceIdRatio() { + this.contextRunner + .withPropertyValues("management.opentelemetry.tracing.sampler=trace-id-ratio", + "management.tracing.sampling.probability=0.3") + .run((context) -> { + Sampler sampler = context.getBean(Sampler.class); + assertThat(sampler.getDescription()).startsWith("TraceIdRatioBased{").contains("0.3"); + }); + } + + @Test + void samplerCanBeSetToParentBasedAlwaysOn() { + this.contextRunner.withPropertyValues("management.opentelemetry.tracing.sampler=parent-based-always-on") + .run((context) -> { + Sampler sampler = context.getBean(Sampler.class); + assertThat(sampler.getDescription()).startsWith("ParentBased{root:AlwaysOnSampler"); + }); + } + + @Test + void samplerCanBeSetToParentBasedAlwaysOff() { + this.contextRunner.withPropertyValues("management.opentelemetry.tracing.sampler=parent-based-always-off") + .run((context) -> { + Sampler sampler = context.getBean(Sampler.class); + assertThat(sampler.getDescription()).startsWith("ParentBased{root:AlwaysOffSampler"); + }); + } + + @Test + void samplerCanBeSetToParentBasedTraceIdRatio() { + this.contextRunner + .withPropertyValues("management.opentelemetry.tracing.sampler=parent-based-trace-id-ratio", + "management.tracing.sampling.probability=0.7") + .run((context) -> { + Sampler sampler = context.getBean(Sampler.class); + assertThat(sampler.getDescription()).startsWith("ParentBased{root:TraceIdRatioBased{").contains("0.7"); + }); + } + @ParameterizedTest @ValueSource(strings = { "io.micrometer.tracing.otel", "io.opentelemetry.sdk", "io.opentelemetry.api" }) void shouldNotSupplyBeansIfDependencyIsMissing(String packageName) {