Skip to content

Commit 9aec85b

Browse files
authored
Fix parent based sampling in tracer (#354)
Currently the sdk `Tracer` implementation will filter out invalid / dropped parent span contexts. This causes the parent based sampler to not function properly as it will then assume that child spans are roots. This patch resolves this issue by adding a `has_active_span` method to the trace context extension, which allows the tracer to distinguish between invalid and unset parent contexts.
1 parent bd69642 commit 9aec85b

File tree

6 files changed

+47
-13
lines changed

6 files changed

+47
-13
lines changed

opentelemetry/src/api/trace/context.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ pub trait TraceContextExt {
4242
/// ```
4343
fn span(&self) -> &dyn crate::trace::Span;
4444

45+
/// Used to see if a span has been marked as active
46+
///
47+
/// This is useful for building tracers.
48+
fn has_active_span(&self) -> bool;
49+
4550
/// Returns a copy of this context with the span context included.
4651
///
4752
/// This is useful for building propagators.
@@ -71,6 +76,10 @@ impl TraceContextExt for Context {
7176
}
7277
}
7378

79+
fn has_active_span(&self) -> bool {
80+
self.get::<Span>().is_some()
81+
}
82+
7483
fn with_remote_span_context(&self, span_context: crate::trace::SpanContext) -> Self {
7584
self.with_value(RemoteSpanContext(span_context))
7685
}

opentelemetry/src/api/trace/noop.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,13 @@ impl trace::Tracer for NoopTracer {
150150
.parent_context
151151
.take()
152152
.or_else(|| {
153-
Some(cx.span().span_context())
154-
.filter(|sc| sc.is_valid())
155-
.cloned()
153+
if cx.has_active_span() {
154+
Some(cx.span().span_context().clone())
155+
} else {
156+
None
157+
}
156158
})
157-
.or_else(|| cx.remote_span_context().filter(|sc| sc.is_valid()).cloned())
158-
.filter(|cx| cx.is_valid());
159+
.or_else(|| cx.remote_span_context().cloned());
159160
if let Some(span_context) = parent_span_context {
160161
trace::NoopSpan { span_context }
161162
} else {

opentelemetry/src/api/trace/span_context.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ impl TraceState {
164164
/// assert!(trace_state.is_ok());
165165
/// assert_eq!(trace_state.unwrap().header(), String::from("foo=bar,apple=banana"))
166166
/// ```
167+
#[allow(clippy::all)]
167168
pub fn from_key_value<T, K, V>(trace_state: T) -> Result<Self, ()>
168169
where
169170
T: IntoIterator<Item = (K, V)>,
@@ -209,6 +210,7 @@ impl TraceState {
209210
/// updated key/value is returned.
210211
///
211212
/// ['spec']: https://www.w3.org/TR/trace-context/#list
213+
#[allow(clippy::all)]
212214
pub fn insert(&self, key: String, value: String) -> Result<TraceState, ()> {
213215
if !TraceState::valid_key(key.as_str()) || !TraceState::valid_value(value.as_str()) {
214216
return Err(());
@@ -227,6 +229,7 @@ impl TraceState {
227229
/// with the removed entry is returned.
228230
///
229231
/// ['spec']: https://www.w3.org/TR/trace-context/#list
232+
#[allow(clippy::all)]
230233
pub fn delete(&self, key: String) -> Result<TraceState, ()> {
231234
if !TraceState::valid_key(key.as_str()) {
232235
return Err(());

opentelemetry/src/sdk/metrics/aggregators/array.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ impl Quantile for PointsData {
205205
return Err(MetricsError::NoDataCollected);
206206
}
207207

208-
if q < 0.0 || q > 1.0 {
208+
if !(0.0..=1.0).contains(&q) {
209209
return Err(MetricsError::InvalidQuantile);
210210
}
211211

opentelemetry/src/sdk/metrics/aggregators/ddsketch.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ impl Distribution for DDSKetchAggregator {}
116116

117117
impl Quantile for DDSKetchAggregator {
118118
fn quantile(&self, q: f64) -> Result<Number> {
119-
if q < 0.0 || q > 1.0 {
119+
if !(0.0..=1.0).contains(&q) {
120120
return Err(MetricsError::InvalidQuantile);
121121
}
122122
self.inner.read().map_err(From::from).and_then(|inner| {

opentelemetry/src/sdk/trace/tracer.rs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,14 @@ impl crate::trace::Tracer for Tracer {
172172
let parent_span_context = builder
173173
.parent_context
174174
.as_ref()
175-
.or_else(|| Some(cx.span().span_context()).filter(|cx| cx.is_valid()))
176-
.or_else(|| cx.remote_span_context())
177-
.filter(|cx| cx.is_valid());
175+
.or_else(|| {
176+
if cx.has_active_span() {
177+
Some(cx.span().span_context())
178+
} else {
179+
None
180+
}
181+
})
182+
.or_else(|| cx.remote_span_context());
178183
// Build context for sampling decision
179184
let (no_parent, trace_id, parent_span_id, remote_parent, parent_trace_flags) =
180185
parent_span_context
@@ -283,11 +288,12 @@ mod tests {
283288
use crate::{
284289
sdk::{
285290
self,
286-
trace::{Config, SamplingDecision, SamplingResult, ShouldSample},
291+
trace::{Config, Sampler, SamplingDecision, SamplingResult, ShouldSample},
287292
},
293+
testing::trace::TestSpan,
288294
trace::{
289-
Link, Span, SpanBuilder, SpanContext, SpanId, SpanKind, TraceId, TraceState, Tracer,
290-
TracerProvider, TRACE_FLAG_SAMPLED,
295+
Link, Span, SpanBuilder, SpanContext, SpanId, SpanKind, TraceContextExt, TraceId,
296+
TraceState, Tracer, TracerProvider, TRACE_FLAG_SAMPLED,
291297
},
292298
Context, KeyValue,
293299
};
@@ -340,4 +346,19 @@ mod tests {
340346
let expected = span_context.trace_state();
341347
assert_eq!(expected.get("foo"), Some("notbar"))
342348
}
349+
350+
#[test]
351+
fn drop_parent_based_children() {
352+
let sampler = Sampler::ParentBased(Box::new(Sampler::AlwaysOn));
353+
let config = Config::default().with_default_sampler(sampler);
354+
let tracer_provider = sdk::trace::TracerProvider::builder()
355+
.with_config(config)
356+
.build();
357+
358+
let context = Context::current_with_span(TestSpan(SpanContext::empty_context()));
359+
let tracer = tracer_provider.get_tracer("test", None);
360+
let span = tracer.start_from_context("must_not_be_sampled", &context);
361+
362+
assert!(!span.span_context().is_sampled());
363+
}
343364
}

0 commit comments

Comments
 (0)