How would you configure a custom sampling strategy in Spring Boot — for example, always sampling a critical endpoint but rate-limiting the rest?
answer
- property = simple; Sampler bean = custom
- ConditionalOnMissingBean backoff
- Brave: RateLimitingSampler.create(n)
- OTel: parentBased(traceIdRatioBased)
- custom bean overrides the property
basics
~10 sFor simple cases set management.tracing.sampling.probability. For custom logic, define your own Sampler bean (Brave's brave.sampler.Sampler or OpenTelemetry's io.opentelemetry.sdk.trace.samplers.Sampler), which overrides the property. Brave offers RateLimitingSampler and per-path samplers you can compose.
solid answer
~40 sSpring Boot auto-configures a Sampler from management.tracing.sampling.probability, but you can replace it by defining your own Sampler bean, which the auto-config backs off from. With the Brave bridge you supply a brave.sampler.Sampler — e.g. RateLimitingSampler.create(tracesPerSecond) for predictable cost, or a custom Sampler that inspects the operation and returns true for critical paths. With the OpenTelemetry bridge you supply an io.opentelemetry.sdk.trace.samplers.Sampler, typically Sampler.parentBased(root) so upstream decisions are honored, wrapping a ratio or a rule-based delegate. The key ideas: the property is the simple knob; a Sampler bean is the escape hatch; use ParentBased/parent-honoring wrappers so you don't break distributed trace coherence; and prefer rate limiting when you need bounded cost under traffic spikes.
code
java · 31 linesimport io.opentelemetry.sdk.trace.samplers.Sampler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
class TracingSamplingConfig {
// OpenTelemetry bridge: always sample /checkout, otherwise 5%,
// and honor any upstream sampling decision via parentBased(...).
@Bean
Sampler otelSampler() {
Sampler fallback = Sampler.traceIdRatioBased(0.05);
Sampler rules = new Sampler() {
@Override public io.opentelemetry.sdk.trace.samplers.SamplingResult shouldSample(
io.opentelemetry.context.Context ctx, String traceId, String name,
io.opentelemetry.api.trace.SpanKind kind,
io.opentelemetry.api.common.Attributes attrs,
java.util.List<io.opentelemetry.api.trace.LinkData> links) {
String route = attrs.get(
io.opentelemetry.api.common.AttributeKey.stringKey("http.route"));
if ("/checkout".equals(route)) {
return io.opentelemetry.sdk.trace.samplers.SamplingResult.recordAndSample();
}
return fallback.shouldSample(ctx, traceId, name, kind, attrs, links);
}
@Override public String getDescription() { return "criticalRouteSampler"; }
};
// Defining this bean overrides management.tracing.sampling.probability.
return Sampler.parentBased(rules); // honor upstream sampled flag for existing traces
}
}go deeper
Know the property is the simple option; custom needs a bean.
Name the two bean types (Brave vs OTel Sampler) and that the bean overrides the property.
Compose rate limiting + route rules + parentBased correctly for the active bridge.
Decide where sampling logic belongs (in-app Sampler vs collector tail sampling) across a fleet.
**Layers of configuration.** 1. **Property (simplest).** `management.tracing.sampling.probability=0.2` — one global ratio. Good enough for most services. 2. **Custom `Sampler` bean (escape hatch).** Spring Boot's tracing auto-configuration creates a default `Sampler` only if you don't define one (`@ConditionalOnMissingBean`). Declaring your own bean overrides the property-driven default entirely, so you own the logic. **Brave bridge (`micrometer-tracing-bridge-brave`).** The bean type is `brave.sampler.Sampler`. - `Sampler.ALWAYS_SAMPLE` / `Sampler.NEVER_SAMPLE` — constants. - `CountingSampler.create(rate)` — probabilistic. - `RateLimitingSampler.create(tracesPerSecond)` — caps traces/second, decoupling cost from traffic (great for spike safety). - A custom `Sampler` overriding `isSampled(long traceId)` for trace-ID-based logic. - For **per-request/per-path** decisions, Brave uses `SamplerFunction<HttpRequest>` on the HTTP tracing handler — this can look at the path/method and decide. **OpenTelemetry bridge (`micrometer-tracing-bridge-otel`).** The bean type is `io.opentelemetry.sdk.trace.samplers.Sampler`. - `Sampler.alwaysOn()`, `Sampler.alwaysOff()`, `Sampler.traceIdRatioBased(ratio)`. - `Sampler.parentBased(root)` — **honor the upstream sampled flag**, falling back to `root` for new traces. This is what preserves distributed-trace coherence; Spring Boot uses a parent-based wrapper by default. - A custom `Sampler` implementing `shouldSample(...)` to inspect span name/attributes and return `RECORD_AND_SAMPLE` for critical operations, delegating otherwise. **Design guidance.** - **Wrap in parent-based** so you don't re-decide on requests that already carry an upstream sampled flag — otherwise you fragment traces. - **Rate limiting** gives bounded cost under spikes; probability does not. - **Route-aware** sampling (always-on critical paths, low on health checks/hot reads) targets your budget where it matters. - When you define a custom `Sampler` bean, `management.tracing.sampling.probability` is **ignored** (your bean wins) — document that so nobody is surprised the property 'does nothing'. **Gotchas.** - Bean type must match the active bridge — a Brave `Sampler` bean does nothing under the OTel bridge and vice versa. - Forgetting `parentBased` on the OTel side means downstream services may override upstream decisions, breaking coherence. - Custom samplers that inspect attributes only see attributes available *at span start*; late-set attributes (like final status) aren't available — that's again the head-based limitation, and true outcome-based selection belongs in a tail-sampling collector.
- Once you define a custom Sampler bean, does management.tracing.sampling.probability still apply?No. The auto-configured default sampler is conditional on no user-defined Sampler; your bean replaces it, so the property is ignored. Document that to avoid confusion.
- Why wrap your OpenTelemetry sampler in Sampler.parentBased(...)?So the service honors an upstream service's existing sampling decision (the incoming sampled flag) and only applies your local logic for brand-new root traces, keeping distributed traces coherent instead of re-deciding at every hop.
- Which sampler gives predictable cost during a traffic spike?Brave's RateLimitingSampler (or an equivalent rate limiter), because it caps traces per second regardless of load. A probability/ratio sampler scales export volume with traffic, so a spike multiplies cost.
saying these in an interview costs you the question
- Providing a Brave Sampler bean while the OTel bridge is active (or vice versa)
- Expecting the probability property to still work after defining a custom Sampler
- Omitting parentBased and thereby breaking distributed trace continuity
- Trying to sample on final HTTP status in a head-based Sampler (not available at span start)