Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
361f2cd
feat: Add Suppression flag to context
cijothomas Mar 18, 2025
ea670ca
simplif
cijothomas Mar 18, 2025
7e64aa9
nit comment
cijothomas Mar 18, 2025
5f55f42
clippy to simplify
cijothomas Mar 18, 2025
23ce8e3
fix perf
cijothomas Mar 18, 2025
84f3727
inline boost
cijothomas Mar 18, 2025
f9678c0
Merge branch 'main' into cijothomas/context-suppress
cijothomas Mar 18, 2025
c2714c1
Merge branch 'main' into cijothomas/context-suppress
lalitb Mar 18, 2025
a4d8c70
Merge branch 'main' into cijothomas/context-suppress
cijothomas Mar 18, 2025
f5ea469
reduce public api
cijothomas Mar 18, 2025
f4bb48f
comment on text
cijothomas Mar 19, 2025
ba4be0a
clip
cijothomas Mar 19, 2025
32d217c
Merge branch 'main' into cijothomas/context-suppress
cijothomas Mar 19, 2025
fb73294
comment
cijothomas Mar 19, 2025
5a29bf5
Merge branch 'main' into cijothomas/context-suppress
cijothomas Mar 22, 2025
9dc674f
fmt and add test expose more methods as required
cijothomas Mar 22, 2025
c043437
rename
cijothomas Mar 22, 2025
5ac4cda
fmts
cijothomas Mar 23, 2025
b474512
Merge branch 'main' into cijothomas/context-suppress
cijothomas Mar 24, 2025
73518bc
remove dummywor
cijothomas Mar 25, 2025
c7ba6a0
Merge branch 'main' into cijothomas/context-suppress
cijothomas Mar 25, 2025
1550d53
improve tests
cijothomas Mar 26, 2025
ce59a31
Merge branch 'main' into cijothomas/context-suppress
cijothomas Mar 26, 2025
0148509
Merge branch 'main' into cijothomas/context-suppress
cijothomas Mar 26, 2025
e13c2c6
Merge branch 'main' into cijothomas/context-suppress
lalitb Mar 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions opentelemetry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ name = "context_attach"
harness = false
required-features = ["tracing"]

[[bench]]
name = "context_suppression"
harness = false

[[bench]]
name = "baggage"
harness = false
Expand Down
66 changes: 66 additions & 0 deletions opentelemetry/benches/context_suppression.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use opentelemetry::Context;

// Run this benchmark with:
// cargo bench --bench context_suppression

// The benchmark results:
// criterion = "0.5.1"
// Hardware: Apple M4 Pro
// Total Number of Cores:   14 (10 performance and 4 efficiency)
// | Benchmark | Time |
// |----------------------------|-----------|
// | enter_suppressed | 9.0 ns |
// | normal_attach | 9.0 ns |
// | is_current_suppressed_false| 750 ps |
// | is_current_suppressed_true | 750 ps |

fn criterion_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("telemetry_suppression");

// Benchmark the cost of entering a suppressed scope
group.bench_function("enter_suppressed", |b| {
b.iter(|| {
let _guard = black_box(Context::enter_suppressed());
let _ = black_box(dummy_work());
});
});

// For comparison - normal context attach
group.bench_function("normal_attach", |b| {
b.iter(|| {
let _guard = black_box(Context::current().attach());
let _ = black_box(dummy_work());
});
});

// Benchmark checking if current is suppressed (when not suppressed)
group.bench_function("is_current_suppressed_false", |b| {
// Make sure we're in a non-suppressed context
let _restore_ctx = Context::current().attach();
b.iter(|| {
let is_suppressed = black_box(Context::is_current_suppressed());
black_box(is_suppressed);
});
});

// Benchmark checking if current is suppressed (when suppressed)
group.bench_function("is_current_suppressed_true", |b| {
// Enter suppressed context for the duration of the benchmark
let _suppressed_guard = Context::enter_suppressed();
b.iter(|| {
let is_suppressed = black_box(Context::is_current_suppressed());
black_box(is_suppressed);
});
});

group.finish();
}

#[inline(never)]
fn dummy_work() -> i32 {
black_box(1 + 1)
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
238 changes: 234 additions & 4 deletions opentelemetry/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ pub struct Context {
#[cfg(feature = "trace")]
pub(crate) span: Option<Arc<SynchronizedSpan>>,
entries: Option<Arc<EntryMap>>,
suppress_telemetry: bool,
}

type EntryMap = HashMap<TypeId, Arc<dyn Any + Sync + Send>, BuildHasherDefault<IdHasher>>;
Expand Down Expand Up @@ -242,6 +243,7 @@ impl Context {
entries,
#[cfg(feature = "trace")]
span: self.span.clone(),
suppress_telemetry: self.suppress_telemetry,
}
}

Expand Down Expand Up @@ -328,19 +330,183 @@ impl Context {
}
}

/// Returns whether telemetry is suppressed in this context.
///
/// # Examples
///
/// ```
/// use opentelemetry::Context;
///
/// let cx = Context::new();
/// assert_eq!(cx.is_telemetry_suppressed(), false);
///
/// let suppressed_cx = cx.with_telemetry_suppressed();
/// assert_eq!(suppressed_cx.is_telemetry_suppressed(), true);
/// ```
pub fn is_telemetry_suppressed(&self) -> bool {
self.suppress_telemetry
}

/// Returns a copy of the context with telemetry suppression enabled.
///
/// # Examples
///
/// ```
/// use opentelemetry::Context;
///
/// let cx = Context::new();
/// assert_eq!(cx.is_telemetry_suppressed(), false);
///
/// let suppressed_cx = cx.with_telemetry_suppressed();
/// assert_eq!(suppressed_cx.is_telemetry_suppressed(), true);
/// ```
pub fn with_telemetry_suppressed(&self) -> Self {
Context {
entries: self.entries.clone(),
#[cfg(feature = "trace")]
span: self.span.clone(),
suppress_telemetry: true,
}
}

/// Returns a copy of the context with telemetry suppression disabled.
///
/// # Examples
///
/// ```
/// use opentelemetry::Context;
///
/// let cx = Context::new().with_telemetry_suppressed();
/// assert_eq!(cx.is_telemetry_suppressed(), true);
///
/// let unsuppressed_cx = cx.with_telemetry_unsuppressed();
/// assert_eq!(unsuppressed_cx.is_telemetry_suppressed(), false);
/// ```
pub fn with_telemetry_unsuppressed(&self) -> Self {
Context {
entries: self.entries.clone(),
#[cfg(feature = "trace")]
span: self.span.clone(),
suppress_telemetry: false,
}
}

/// Returns a clone of the current thread's context with telemetry suppression enabled.
///
/// This is a more efficient form of `Context::current().with_telemetry_suppressed()`
/// as it avoids the intermediate context clone.
///
/// # Examples
///
/// ```
/// use opentelemetry::Context;
///
/// // Get a suppressed context based on the current one
/// let suppressed = Context::current_with_telemetry_suppressed();
/// assert_eq!(suppressed.is_telemetry_suppressed(), true);
/// ```
pub fn current_with_telemetry_suppressed() -> Self {
Self::map_current(|cx| cx.with_telemetry_suppressed())
}

/// Returns a clone of the current thread's context with telemetry suppression disabled.
///
/// This is a more efficient form of `Context::current().with_telemetry_unsuppressed()`
/// as it avoids the intermediate context clone.
///
/// # Examples
///
/// ```
/// use opentelemetry::Context;
///
/// // Get an unsuppressed context based on the current one
/// let unsuppressed = Context::current_with_telemetry_unsuppressed();
/// assert_eq!(unsuppressed.is_telemetry_suppressed(), false);
/// ```
pub fn current_with_telemetry_unsuppressed() -> Self {
Self::map_current(|cx| cx.with_telemetry_unsuppressed())
}

/// Enters a scope where telemetry is suppressed.
///
/// This is a convenience method that creates a new context with telemetry
/// suppression enabled and attaches it.
///
/// # Examples
///
/// ```
/// use opentelemetry::Context;
///
/// let _guard = Context::enter_suppressed();
/// assert_eq!(Context::current().is_telemetry_suppressed(), true);
/// ```
pub fn enter_suppressed() -> ContextGuard {
Self::current_with_telemetry_suppressed().attach()
}

/// Returns whether telemetry is suppressed in the current context.
///
/// This is a convenience method that combines `Context::current()` and
/// `is_telemetry_suppressed()`.
///
/// # Examples
///
/// ```
/// use opentelemetry::Context;
///
/// // Default context has telemetry suppression disabled
/// assert_eq!(Context::is_current_suppressed(), false);
///
/// // Enter a suppressed scope
/// let _guard = Context::enter_suppressed();
/// assert_eq!(Context::is_current_suppressed(), true);
/// ```
#[inline]
pub fn is_current_suppressed() -> bool {
Self::map_current(|cx| cx.is_telemetry_suppressed())
}

/// Enters a scope where telemetry is not suppressed.
///
/// This is a convenience method that creates a new context with telemetry
/// suppression disabled and attaches it.
///
/// # Examples
///
/// ```
/// use opentelemetry::Context;
///
/// let _guard = Context::enter_suppressed();
/// assert_eq!(Context::current().is_telemetry_suppressed(), true);
///
/// // Inside a suppressed scope, create an unsuppressed one
/// {
/// let _inner_guard = Context::enter_unsuppressed();
/// assert_eq!(Context::current().is_telemetry_suppressed(), false);
/// }
///
/// // After inner guard is dropped, we're back to the suppressed scope
/// assert_eq!(Context::current().is_telemetry_suppressed(), true);
/// ```
pub fn enter_unsuppressed() -> ContextGuard {
Self::current_with_telemetry_unsuppressed().attach()
}

#[cfg(feature = "trace")]
pub(crate) fn current_with_synchronized_span(value: SynchronizedSpan) -> Self {
Context {
Self::map_current(|cx| Context {
span: Some(Arc::new(value)),
entries: Context::map_current(|cx| cx.entries.clone()),
}
entries: cx.entries.clone(),
suppress_telemetry: cx.suppress_telemetry,
})
}

#[cfg(feature = "trace")]
pub(crate) fn with_synchronized_span(&self, value: SynchronizedSpan) -> Self {
Context {
span: Some(Arc::new(value)),
entries: self.entries.clone(),
suppress_telemetry: self.suppress_telemetry,
}
}
}
Expand All @@ -359,7 +525,9 @@ impl fmt::Debug for Context {
}
}

dbg.field("entries count", &entries).finish()
dbg.field("entries count", &entries)
.field("suppress_telemetry", &self.suppress_telemetry)
.finish()
}
}

Expand Down Expand Up @@ -669,4 +837,66 @@ mod tests {
stack.pop_id(ContextStack::MAX_POS);
stack.pop_id(4711);
}

#[test]
fn telemetry_suppression() {
// Default context has telemetry suppression disabled
let cx = Context::new();
assert!(!cx.is_telemetry_suppressed());

// With suppression enabled
let suppressed = cx.with_telemetry_suppressed();
assert!(suppressed.is_telemetry_suppressed());

// With suppression disabled again
let unsuppressed = suppressed.with_telemetry_unsuppressed();
assert!(!unsuppressed.is_telemetry_suppressed());

// Add a value while preserving suppression state
let suppressed_with_value = suppressed.with_value(ValueA(1));
assert!(suppressed_with_value.is_telemetry_suppressed());
assert_eq!(suppressed_with_value.get::<ValueA>(), Some(&ValueA(1)));

// Test entering a suppressed scope
{
let _guard = Context::enter_suppressed();
assert!(Context::current().is_telemetry_suppressed());

// Test nested scopes with different suppression states
{
let _inner_guard = Context::enter_unsuppressed();
assert!(!Context::current().is_telemetry_suppressed());
}

// Back to suppressed
assert!(Context::current().is_telemetry_suppressed());
}

// Back to default
assert!(!Context::current().is_telemetry_suppressed());
}

#[test]
fn is_current_suppressed_test() {
// Default context has telemetry suppression disabled
assert!(!Context::is_current_suppressed());

// Enter a suppressed scope
{
let _guard = Context::enter_suppressed();
assert!(Context::is_current_suppressed());

// Test nested scopes with different suppression states
{
let _inner_guard = Context::enter_unsuppressed();
assert!(!Context::is_current_suppressed());
}

// Back to suppressed
assert!(Context::is_current_suppressed());
}

// Back to default
assert!(!Context::is_current_suppressed());
}
}
4 changes: 2 additions & 2 deletions opentelemetry/src/trace/span_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ mod tests {
let cx = Context::current();
assert_eq!(
format!("{:?}", cx),
"Context { span: \"None\", entries count: 0 }"
"Context { span: \"None\", entries count: 0, suppress_telemetry: false }"
);
let cx = Context::current().with_remote_span_context(SpanContext::NONE);
assert_eq!(
Expand All @@ -413,7 +413,7 @@ mod tests {
is_remote: false, \
trace_state: TraceState(None) \
}, \
entries count: 1 \
entries count: 1, suppress_telemetry: false \
}"
);
}
Expand Down