Repository navigation
[Bug]: BatchSpanProcessor exports spans that are recorded but not sampled #3812
Description
What happened?
BatchSpanProcessor sends spans with IsRecording = true and Sampled = false to the exporter. SimpleSpanProcessor drops these spans.
The trace SDK specification says that the exporter SHOULD NOT receive these spans (Sampling, Recording Sampled reaction table):
[...] However, Span Exporter SHOULD NOT receive them unless the Sampled flag was also set.
Steps to reproduce:
- Configure a sampler that returns SamplingDecision::RecordOnly.
- Add a SimpleSpanProcessor and a BatchSpanProcessor, each with an InMemorySpanExporter.
- Create and end one span. Call force_flush.
/// Records every span, but samples none.
#[derive(Clone, Debug)]
struct RecordOnly;
impl ShouldSample for RecordOnly {
fn should_sample(
&self,
_parent_context: Option<&Context>,
_trace_id: TraceId,
_name: &str,
_span_kind: &SpanKind,
_attributes: &[KeyValue],
_links: &[Link],
) -> SamplingResult {
SamplingResult {
decision: SamplingDecision::RecordOnly,
attributes: Vec::new(),
trace_state: TraceState::default(),
}
}
}
fn main() {
let simple = InMemorySpanExporter::default();
let batch = InMemorySpanExporter::default();
let provider = SdkTracerProvider::builder()
.with_sampler(RecordOnly)
.with_span_processor(SimpleSpanProcessor::new(simple.clone()))
.with_span_processor(BatchSpanProcessor::builder(batch.clone()).build())
.build();
provider.tracer("repro").in_span("record-only", |_cx| {});
provider.force_flush().unwrap();
let simple = simple.get_finished_spans().unwrap();
let batch = batch.get_finished_spans().unwrap();
println!("SimpleSpanProcessor exported {} span(s)", simple.len());
println!(
"BatchSpanProcessor exported {} span(s), sampled = {:?}",
batch.len(),
batch.iter().map(|s| s.span_context.is_sampled()).collect::<Vec<_>>()
);
}Output:
SimpleSpanProcessor exported 0 span(s)
BatchSpanProcessor exported 1 span(s), sampled = [false]
Expected: BatchSpanProcessor exports 0 spans, the same as SimpleSpanProcessor.
SimpleSpanProcessor::on_end in opentelemetry-sdk/src/trace/span_processor.rs returns early when span.span_context.is_sampled() is false. The
BatchSpanProcessor in span_processor_with_async_runtime.rs does the same check. BatchSpanProcessor::on_end in span_processor.rs does not do this check.
OpenTelemetry API Version (i.e version of opentelemetry crate)
0.33.1
OpenTelemetry SDK Version (i.e version of opentelemetry_sdk crate)
0.33.1
What Exporter(s) are you seeing the problem on?
N/A
Relevant log output
Tip
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.
Activity