Skip to main content

opentelemetry/
context.rs

1//! Execution-scoped context propagation.
2//!
3//! The `context` module provides mechanisms for propagating values across API boundaries and between
4//! logically associated execution units. It enables cross-cutting concerns to access their data in-process
5//! using a shared context object.
6//!
7//! # Main Types
8//!
9//! - [`Context`]: An immutable, execution-scoped collection of values.
10//!
11
12use crate::otel_warn;
13#[cfg(feature = "trace")]
14use crate::trace::context::SynchronizedSpan;
15use std::any::{Any, TypeId};
16use std::cell::RefCell;
17use std::collections::HashMap;
18use std::fmt;
19use std::hash::{BuildHasherDefault, Hasher};
20use std::marker::PhantomData;
21use std::sync::Arc;
22#[cfg(feature = "experimental_context_observer")]
23use std::sync::OnceLock;
24
25#[cfg(feature = "futures")]
26mod future_ext;
27
28#[cfg(feature = "futures")]
29pub use future_ext::{FutureExt, WithContext};
30
31#[cfg(feature = "experimental_context_observer")]
32pub use context_observer::*;
33
34thread_local! {
35    static CURRENT_CONTEXT: RefCell<ContextStack> = RefCell::new(ContextStack::default());
36}
37
38/// An execution-scoped collection of values.
39///
40/// A [`Context`] is a propagation mechanism which carries execution-scoped
41/// values across API boundaries and between logically associated execution
42/// units. Cross-cutting concerns access their data in-process using the same
43/// shared context object.
44///
45/// [`Context`]s are immutable, and their write operations result in the creation
46/// of a new context containing the original values and the new specified values.
47///
48/// ## Context state
49///
50/// Concerns can create and retrieve their local state in the current execution
51/// state represented by a context through the [`get`] and [`with_value`]
52/// methods. It is recommended to use application-specific types when storing new
53/// context values to avoid unintentionally overwriting existing state.
54///
55/// ## Managing the current context
56///
57/// Contexts can be associated with the caller's current execution unit on a
58/// given thread via the [`attach`] method, and previous contexts can be restored
59/// by dropping the returned [`ContextGuard`]. Context can be nested, and will
60/// restore their parent outer context when detached on drop. To access the
61/// values of the context, a snapshot can be created via the [`Context::current`]
62/// method.
63///
64/// [`Context::current`]: Context::current()
65/// [`get`]: Context::get()
66/// [`with_value`]: Context::with_value()
67/// [`attach`]: Context::attach()
68///
69/// # Examples
70///
71/// ```
72/// use opentelemetry::Context;
73///
74/// // Application-specific `a` and `b` values
75/// #[derive(Debug, PartialEq)]
76/// struct ValueA(&'static str);
77/// #[derive(Debug, PartialEq)]
78/// struct ValueB(u64);
79///
80/// let _outer_guard = Context::new().with_value(ValueA("a")).attach();
81///
82/// // Only value a has been set
83/// let current = Context::current();
84/// assert_eq!(current.get::<ValueA>(), Some(&ValueA("a")));
85/// assert_eq!(current.get::<ValueB>(), None);
86///
87/// {
88///     let _inner_guard = Context::current_with_value(ValueB(42)).attach();
89///     // Both values are set in inner context
90///     let current = Context::current();
91///     assert_eq!(current.get::<ValueA>(), Some(&ValueA("a")));
92///     assert_eq!(current.get::<ValueB>(), Some(&ValueB(42)));
93/// }
94///
95/// // Resets to only the `a` value when inner guard is dropped
96/// let current = Context::current();
97/// assert_eq!(current.get::<ValueA>(), Some(&ValueA("a")));
98/// assert_eq!(current.get::<ValueB>(), None);
99/// ```
100#[derive(Clone, Default)]
101pub struct Context {
102    #[cfg(feature = "trace")]
103    pub(crate) span: Option<Arc<SynchronizedSpan>>,
104    entries: Option<Arc<EntryMap>>,
105    suppress_telemetry: bool,
106    /// A context's observer view of this [Context].
107    ///
108    /// The main application for observers is to share information from the current context with
109    /// external readers (e.g. a full host eBPF profiler). In that case, the observer will attach
110    /// and detach some data on context events using its own mechanism. The shared data has no
111    /// reason to match our internal representation ([Context]).
112    ///
113    /// [observer_view] makes it possible to store the observer's view of the context alongside a
114    /// [Context] value, such that it can be attached and detached on context events.
115    #[cfg(feature = "experimental_context_observer")]
116    observer_view: Option<Arc<dyn ObserverContextView>>,
117}
118
119type EntryMap = HashMap<TypeId, Arc<dyn Any + Sync + Send>, BuildHasherDefault<IdHasher>>;
120
121impl Context {
122    /// Creates an empty `Context`.
123    ///
124    /// The context is initially created with a capacity of 0, so it will not
125    /// allocate. Use [`with_value`] to create a new context that has entries.
126    ///
127    /// [`with_value`]: Context::with_value()
128    pub fn new() -> Self {
129        Context::default()
130    }
131
132    /// Returns an immutable snapshot of the current thread's context.
133    ///
134    /// # Behavior During Context Drop
135    ///
136    /// When called from within a [`Drop`] implementation that is triggered by
137    /// a [`ContextGuard`] being dropped (e.g., when a [`Span`] is dropped as part
138    /// of context cleanup), this function returns **whatever context happens to be
139    /// current after the guard is popped**, not the context being dropped.
140    ///
141    /// **Important**: The returned context may be completely unrelated to the
142    /// context being dropped, as contexts can be activated in any order and are
143    /// not necessarily hierarchical. Do not rely on any relationship between them.
144    ///
145    /// This behavior is by design and prevents panics that would otherwise occur
146    /// from attempting to borrow the context while it's being mutably borrowed
147    /// for cleanup. See [issue #2871](https://github.com/open-telemetry/opentelemetry-rust/issues/2871)
148    /// for details.
149    ///
150    /// # Examples
151    ///
152    /// ```
153    /// use opentelemetry::Context;
154    ///
155    /// #[derive(Debug, PartialEq)]
156    /// struct ValueA(&'static str);
157    ///
158    /// fn do_work() {
159    ///     assert_eq!(Context::current().get(), Some(&ValueA("a")));
160    /// }
161    ///
162    /// let _guard = Context::new().with_value(ValueA("a")).attach();
163    /// do_work()
164    /// ```
165    ///
166    /// [`Span`]: crate::trace::Span
167    pub fn current() -> Self {
168        Self::map_current(|cx| cx.clone())
169    }
170
171    /// Applies a function to the current context returning its value.
172    ///
173    /// This can be used to build higher performing algebraic expressions for
174    /// optionally creating a new context without the overhead of cloning the
175    /// current one and dropping it.
176    ///
177    /// Note: This function will panic if you attempt to attach another context
178    /// while the current one is still borrowed.
179    pub fn map_current<T>(f: impl FnOnce(&Context) -> T) -> T {
180        CURRENT_CONTEXT.with(|cx| cx.borrow().map_current_cx(f))
181    }
182
183    /// Returns a clone of the current thread's context with the given value.
184    ///
185    /// This is a more efficient form of `Context::current().with_value(value)`
186    /// as it avoids the intermediate context clone.
187    ///
188    /// # Examples
189    ///
190    /// ```
191    /// use opentelemetry::Context;
192    ///
193    /// // Given some value types defined in your application
194    /// #[derive(Debug, PartialEq)]
195    /// struct ValueA(&'static str);
196    /// #[derive(Debug, PartialEq)]
197    /// struct ValueB(u64);
198    ///
199    /// // You can create and attach context with the first value set to "a"
200    /// let _guard = Context::new().with_value(ValueA("a")).attach();
201    ///
202    /// // And create another context based on the fist with a new value
203    /// let all_current_and_b = Context::current_with_value(ValueB(42));
204    ///
205    /// // The second context now contains all the current values and the addition
206    /// assert_eq!(all_current_and_b.get::<ValueA>(), Some(&ValueA("a")));
207    /// assert_eq!(all_current_and_b.get::<ValueB>(), Some(&ValueB(42)));
208    /// ```
209    pub fn current_with_value<T: 'static + Send + Sync>(value: T) -> Self {
210        Self::map_current(|cx| cx.with_value(value))
211    }
212
213    /// Returns a reference to the entry for the corresponding value type.
214    ///
215    /// # Examples
216    ///
217    /// ```
218    /// use opentelemetry::Context;
219    ///
220    /// // Given some value types defined in your application
221    /// #[derive(Debug, PartialEq)]
222    /// struct ValueA(&'static str);
223    /// #[derive(Debug, PartialEq)]
224    /// struct MyUser();
225    ///
226    /// let cx = Context::new().with_value(ValueA("a"));
227    ///
228    /// // Values can be queried by type
229    /// assert_eq!(cx.get::<ValueA>(), Some(&ValueA("a")));
230    ///
231    /// // And return none if not yet set
232    /// assert_eq!(cx.get::<MyUser>(), None);
233    /// ```
234    pub fn get<T: 'static>(&self) -> Option<&T> {
235        self.entries
236            .as_ref()?
237            .get(&TypeId::of::<T>())?
238            .downcast_ref()
239    }
240
241    /// Returns a copy of the context with the new value included.
242    ///
243    /// # Examples
244    ///
245    /// ```
246    /// use opentelemetry::Context;
247    ///
248    /// // Given some value types defined in your application
249    /// #[derive(Debug, PartialEq)]
250    /// struct ValueA(&'static str);
251    /// #[derive(Debug, PartialEq)]
252    /// struct ValueB(u64);
253    ///
254    /// // You can create a context with the first value set to "a"
255    /// let cx_with_a = Context::new().with_value(ValueA("a"));
256    ///
257    /// // And create another context based on the fist with a new value
258    /// let cx_with_a_and_b = cx_with_a.with_value(ValueB(42));
259    ///
260    /// // The first context is still available and unmodified
261    /// assert_eq!(cx_with_a.get::<ValueA>(), Some(&ValueA("a")));
262    /// assert_eq!(cx_with_a.get::<ValueB>(), None);
263    ///
264    /// // The second context now contains both values
265    /// assert_eq!(cx_with_a_and_b.get::<ValueA>(), Some(&ValueA("a")));
266    /// assert_eq!(cx_with_a_and_b.get::<ValueB>(), Some(&ValueB(42)));
267    /// ```
268    pub fn with_value<T: 'static + Send + Sync>(&self, value: T) -> Self {
269        let entries = if let Some(current_entries) = &self.entries {
270            let mut inner_entries = (**current_entries).clone();
271            inner_entries.insert(TypeId::of::<T>(), Arc::new(value));
272            Some(Arc::new(inner_entries))
273        } else {
274            let mut entries = EntryMap::default();
275            entries.insert(TypeId::of::<T>(), Arc::new(value));
276            Some(Arc::new(entries))
277        };
278
279        Context {
280            entries,
281            #[cfg(feature = "trace")]
282            span: self.span.clone(),
283            suppress_telemetry: self.suppress_telemetry,
284            #[cfg(feature = "experimental_context_observer")]
285            observer_view: None,
286        }
287        .with_observer_view()
288    }
289
290    /// Replaces the current context on this thread with this context.
291    ///
292    /// Dropping the returned [`ContextGuard`] will reset the current context to the
293    /// previous value.
294    ///
295    ///
296    /// # Examples
297    ///
298    /// ```
299    /// use opentelemetry::Context;
300    ///
301    /// #[derive(Debug, PartialEq)]
302    /// struct ValueA(&'static str);
303    ///
304    /// let my_cx = Context::new().with_value(ValueA("a"));
305    ///
306    /// // Set the current thread context
307    /// let cx_guard = my_cx.attach();
308    /// assert_eq!(Context::current().get::<ValueA>(), Some(&ValueA("a")));
309    ///
310    /// // Drop the guard to restore the previous context
311    /// drop(cx_guard);
312    /// assert_eq!(Context::current().get::<ValueA>(), None);
313    /// ```
314    ///
315    /// Guards do not need to be explicitly dropped:
316    ///
317    /// ```
318    /// use opentelemetry::Context;
319    ///
320    /// #[derive(Debug, PartialEq)]
321    /// struct ValueA(&'static str);
322    ///
323    /// fn my_function() -> String {
324    ///     // attach a context the duration of this function.
325    ///     let my_cx = Context::new().with_value(ValueA("a"));
326    ///     // NOTE: a variable name after the underscore is **required** or rust
327    ///     // will drop the guard, restoring the previous context _immediately_.
328    ///     let _guard = my_cx.attach();
329    ///
330    ///     // anything happening in functions we call can still access my_cx...
331    ///     my_other_function();
332    ///
333    ///     // returning from the function drops the guard, exiting the span.
334    ///     return "Hello world".to_owned();
335    /// }
336    ///
337    /// fn my_other_function() {
338    ///     // ...
339    /// }
340    /// ```
341    /// Sub-scopes may be created to limit the duration for which the span is
342    /// entered:
343    ///
344    /// ```
345    /// use opentelemetry::Context;
346    ///
347    /// #[derive(Debug, PartialEq)]
348    /// struct ValueA(&'static str);
349    ///
350    /// let my_cx = Context::new().with_value(ValueA("a"));
351    ///
352    /// {
353    ///     let _guard = my_cx.attach();
354    ///
355    ///     // the current context can access variables in
356    ///     assert_eq!(Context::current().get::<ValueA>(), Some(&ValueA("a")));
357    ///
358    ///     // exiting the scope drops the guard, detaching the context.
359    /// }
360    ///
361    /// // this is back in the default empty context
362    /// assert_eq!(Context::current().get::<ValueA>(), None);
363    /// ```
364    pub fn attach(self) -> ContextGuard {
365        let cx_id = CURRENT_CONTEXT.with(|cx| cx.borrow_mut().push(self));
366
367        ContextGuard {
368            cx_pos: cx_id,
369            _marker: PhantomData,
370        }
371    }
372
373    /// Returns whether telemetry is suppressed in this context.
374    #[inline]
375    pub fn is_telemetry_suppressed(&self) -> bool {
376        self.suppress_telemetry
377    }
378
379    /// Returns a new context with telemetry suppression enabled.
380    pub fn with_telemetry_suppressed(&self) -> Self {
381        Context {
382            entries: self.entries.clone(),
383            #[cfg(feature = "trace")]
384            span: self.span.clone(),
385            suppress_telemetry: true,
386            #[cfg(feature = "experimental_context_observer")]
387            observer_view: None,
388        }
389        .with_observer_view()
390    }
391
392    /// Enters a scope where telemetry is suppressed.
393    ///
394    /// This method is specifically designed for OpenTelemetry components (like Exporters,
395    /// Processors etc.) to prevent generating recursive or self-referential
396    /// telemetry data when performing their own operations.
397    ///
398    /// Without suppression, we have a telemetry-induced-telemetry situation
399    /// where, operations like exporting telemetry could generate new telemetry
400    /// about the export process itself, potentially causing:
401    /// - Infinite telemetry feedback loops
402    /// - Excessive resource consumption
403    ///
404    /// This method:
405    /// 1. Takes the current context
406    /// 2. Creates a new context from current, with `suppress_telemetry` set to `true`
407    /// 3. Attaches it to the current thread
408    /// 4. Returns a guard that restores the previous context when dropped
409    ///
410    /// OTel SDK components would check `is_current_telemetry_suppressed()` before
411    /// generating new telemetry, but not end users.
412    ///
413    /// # Examples
414    ///
415    /// ```
416    /// use opentelemetry::Context;
417    ///
418    /// // Example: Inside an exporter's implementation
419    /// fn example_export_function() {
420    ///     // Prevent telemetry-generating operations from creating more telemetry
421    ///     let _guard = Context::enter_telemetry_suppressed_scope();
422    ///     
423    ///     // Verify suppression is active
424    ///     assert_eq!(Context::is_current_telemetry_suppressed(), true);
425    ///     
426    ///     // Here you would normally perform operations that might generate telemetry
427    ///     // but now they won't because the context has suppression enabled
428    /// }
429    ///
430    /// // Demonstrate the function
431    /// example_export_function();
432    /// ```
433    pub fn enter_telemetry_suppressed_scope() -> ContextGuard {
434        Self::map_current(|cx| cx.with_telemetry_suppressed()).attach()
435    }
436
437    /// Returns whether telemetry is suppressed in the current context.
438    ///
439    /// This method is used by OpenTelemetry components to determine whether they should
440    /// generate new telemetry in the current execution context. It provides a performant
441    /// way to check the suppression state.
442    ///
443    /// End-users generally should not use this method directly, as it is primarily intended for
444    /// OpenTelemetry SDK components.
445    ///
446    ///
447    #[inline]
448    pub fn is_current_telemetry_suppressed() -> bool {
449        Self::map_current(|cx| cx.is_telemetry_suppressed())
450    }
451
452    // Initialize `self.observer_view` for a newly created context using the current context
453    // observer, if set.
454    #[cfg(feature = "experimental_context_observer")]
455    fn with_observer_view(mut self) -> Self {
456        if let Some(observer) = GlobalContextObserver::get() {
457            self.observer_view = observer.make_view(&self);
458        }
459
460        self
461    }
462
463    // No-op when the experimental_context_observer feature is not enabled.
464    #[cfg(not(feature = "experimental_context_observer"))]
465    const fn with_observer_view(self) -> Self {
466        self
467    }
468
469    /// Returns the observer's view of this context, if any.
470    #[cfg(feature = "experimental_context_observer")]
471    #[inline]
472    pub fn observer_view(&self) -> &Option<Arc<dyn ObserverContextView>> {
473        &self.observer_view
474    }
475
476    #[cfg(feature = "trace")]
477    pub(crate) fn current_with_synchronized_span(value: SynchronizedSpan) -> Self {
478        Self::map_current(|cx| {
479            Context {
480                span: Some(Arc::new(value)),
481                entries: cx.entries.clone(),
482                suppress_telemetry: cx.suppress_telemetry,
483                #[cfg(feature = "experimental_context_observer")]
484                observer_view: None,
485            }
486            .with_observer_view()
487        })
488    }
489
490    #[cfg(feature = "trace")]
491    pub(crate) fn with_synchronized_span(&self, value: SynchronizedSpan) -> Self {
492        Context {
493            span: Some(Arc::new(value)),
494            entries: self.entries.clone(),
495            suppress_telemetry: self.suppress_telemetry,
496            #[cfg(feature = "experimental_context_observer")]
497            observer_view: None,
498        }
499        .with_observer_view()
500    }
501}
502
503impl fmt::Debug for Context {
504    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
505        let mut dbg = f.debug_struct("Context");
506
507        #[cfg(feature = "trace")]
508        let mut entries = self.entries.as_ref().map_or(0, |e| e.len());
509        #[cfg(feature = "trace")]
510        {
511            if let Some(span) = &self.span {
512                dbg.field("span", &span.span_context());
513                entries += 1;
514            } else {
515                dbg.field("span", &"None");
516            }
517        }
518        #[cfg(not(feature = "trace"))]
519        let entries = self.entries.as_ref().map_or(0, |e| e.len());
520
521        dbg.field("entries count", &entries)
522            .field("suppress_telemetry", &self.suppress_telemetry)
523            .finish()
524    }
525}
526
527/// A guard that resets the current context to the prior context when dropped.
528#[derive(Debug)]
529pub struct ContextGuard {
530    // The position of the context in the stack. This is used to pop the context.
531    cx_pos: u16,
532    // Ensure this type is !Send as it relies on thread locals
533    _marker: PhantomData<*const ()>,
534}
535
536impl Drop for ContextGuard {
537    fn drop(&mut self) {
538        let id = self.cx_pos;
539        if id > ContextStack::BASE_POS && id < ContextStack::MAX_POS {
540            // Defer destructors that may call back into Context until after
541            // the mutable borrow of CURRENT_CONTEXT has been released.
542            let _to_drop =
543                CURRENT_CONTEXT.with(|context_stack| context_stack.borrow_mut().pop_id(id));
544        }
545    }
546}
547
548/// With TypeIds as keys, there's no need to hash them. They are already hashes
549/// themselves, coming from the compiler. The IdHasher holds the u64 of
550/// the TypeId, and then returns it, instead of doing any bit fiddling.
551#[derive(Clone, Default, Debug)]
552struct IdHasher(u64);
553
554impl Hasher for IdHasher {
555    fn write(&mut self, _: &[u8]) {
556        unreachable!("TypeId calls write_u64");
557    }
558
559    #[inline]
560    fn write_u64(&mut self, id: u64) {
561        self.0 = id;
562    }
563
564    #[inline]
565    fn finish(&self) -> u64 {
566        self.0
567    }
568}
569
570/// A stack for keeping track of the [`Context`] instances that have been attached
571/// to a thread.
572///
573/// The stack allows for popping of contexts by position, which is used to do out
574/// of order dropping of [`ContextGuard`] instances. Only when the top of the
575/// stack is popped, the topmost [`Context`] is actually restored.
576///
577/// The stack relies on the fact that it is thread local and that the
578/// [`ContextGuard`] instances that are constructed using ids from it can't be
579/// moved to other threads. That means that the ids are always valid and that
580/// they are always within the bounds of the stack.
581struct ContextStack {
582    /// This is the current [`Context`] that is active on this thread, and the top
583    /// of the [`ContextStack`]. It is always present, and if the `stack` is empty
584    /// it's an empty [`Context`].
585    ///
586    /// Having this here allows for fast access to the current [`Context`].
587    current_cx: Context,
588    /// A `stack` of the other contexts that have been attached to the thread.
589    stack: Vec<Option<Context>>,
590    /// Ensure this type is !Send as it relies on thread locals
591    _marker: PhantomData<*const ()>,
592}
593
594// Fields whose destructors must run after releasing the mutable
595// borrow of CURRENT_CONTEXT.
596#[derive(Default)]
597struct ContextToDrop {
598    #[cfg(feature = "trace")]
599    _span: Option<Arc<SynchronizedSpan>>,
600    _entries: Option<Arc<EntryMap>>,
601    #[cfg(feature = "experimental_context_observer")]
602    _observer_view: Option<Arc<dyn ObserverContextView>>,
603}
604
605impl From<Context> for ContextToDrop {
606    fn from(context: Context) -> Self {
607        Self {
608            #[cfg(feature = "trace")]
609            _span: context.span,
610            _entries: context.entries,
611            #[cfg(feature = "experimental_context_observer")]
612            _observer_view: context.observer_view,
613        }
614    }
615}
616
617impl ContextStack {
618    const BASE_POS: u16 = 0;
619    const MAX_POS: u16 = u16::MAX;
620    const INITIAL_CAPACITY: usize = 8;
621
622    #[inline(always)]
623    fn push(&mut self, cx: Context) -> u16 {
624        // The next id is the length of the `stack`, plus one since we have the
625        // top of the [`ContextStack`] as the `current_cx`.
626        let next_id = self.stack.len() + 1;
627        if next_id < ContextStack::MAX_POS.into() {
628            #[cfg(feature = "experimental_context_observer")]
629            if let Some(observer) = GlobalContextObserver::get() {
630                observer.on_context_enter(&self.current_cx, &cx);
631            }
632
633            let current_cx = std::mem::replace(&mut self.current_cx, cx);
634            self.stack.push(Some(current_cx));
635            next_id as u16
636        } else {
637            // This is an overflow, log it and ignore it.
638            otel_warn!(
639                name: "Context.AttachFailed",
640                message = format!("Too many contexts. Max limit is {}. \
641                  Context::current() remains unchanged as this attach failed. \
642                  Dropping the returned ContextGuard will have no impact on Context::current().",
643                  ContextStack::MAX_POS)
644            );
645            ContextStack::MAX_POS
646        }
647    }
648
649    #[inline(always)]
650    fn pop_id(&mut self, pos: u16) -> ContextToDrop {
651        if pos == ContextStack::BASE_POS || pos == ContextStack::MAX_POS {
652            // The empty context is always at the bottom of the [`ContextStack`]
653            // and cannot be popped, and the overflow position is invalid, so do
654            // nothing.
655            otel_warn!(
656                name: "Context.OutOfOrderDrop",
657                position = pos,
658                message = if pos == ContextStack::BASE_POS {
659                    "Attempted to pop the base context which is not allowed"
660                } else {
661                    "Attempted to pop the overflow position which is not allowed"
662                }
663            );
664
665            return ContextToDrop::default();
666        }
667        let len: u16 = self.stack.len() as u16;
668        // Are we at the top of the [`ContextStack`]?
669        if pos == len {
670            // Shrink the stack if possible to clear out any out of order pops.
671            while let Some(None) = self.stack.last() {
672                _ = self.stack.pop();
673            }
674            // Restore the previous context. This will always happen since the
675            // empty context is always at the bottom of the stack if the
676            // [`ContextStack`] is not empty.
677            if let Some(Some(next_cx)) = self.stack.pop() {
678                #[cfg(feature = "experimental_context_observer")]
679                if let Some(observer) = GlobalContextObserver::get() {
680                    observer.on_context_exit(&self.current_cx, &next_cx);
681                }
682
683                // Move fields with potentially reentrant destructors out of the borrow.
684                let old_cx = std::mem::replace(&mut self.current_cx, next_cx);
685                return old_cx.into();
686            }
687
688            ContextToDrop::default()
689        } else {
690            // This is an out of order pop.
691            if pos >= len {
692                // This is an invalid id, ignore it.
693                otel_warn!(
694                    name: "Context.PopOutOfBounds",
695                    position = pos,
696                    stack_length = len,
697                    message = "Attempted to pop beyond the end of the context stack"
698                );
699                return ContextToDrop::default();
700            }
701            // Remove the context and defer dropping its owned fields.
702            self.stack[pos as usize]
703                .take()
704                .map(ContextToDrop::from)
705                .unwrap_or_default()
706        }
707    }
708
709    #[inline(always)]
710    fn map_current_cx<T>(&self, f: impl FnOnce(&Context) -> T) -> T {
711        f(&self.current_cx)
712    }
713}
714
715impl Default for ContextStack {
716    fn default() -> Self {
717        ContextStack {
718            current_cx: Context::default(),
719            stack: Vec::with_capacity(ContextStack::INITIAL_CAPACITY),
720            _marker: PhantomData,
721        }
722    }
723}
724
725#[cfg(feature = "experimental_context_observer")]
726mod context_observer {
727    use super::*;
728
729    /// An observer trait for monitoring context transitions.
730    ///
731    /// Implementors of this trait can observe when a context is entered or exited, allowing for
732    /// custom logic to be executed during context switches.
733    ///
734    /// An observer-specific view can be attached to a [`Context`] via
735    /// [`Context::observer_view`]. A view is usually a different representation of the context
736    /// to be published through an alternative channel, but in essence it is arbitrary data computed
737    /// from the context.
738    ///
739    /// # Refcell already borrowed panic
740    ///
741    /// [crate::context] maintains thread-local, internal state in a `RefCell`. This implementation
742    /// detail leaks when the `experimental_context_observer` feature is enabled:
743    /// [Self::on_context_enter] and [Self::on_context_exit] are called from a point where the cell
744    /// is currently borrowed. If a [ContextObserver] implementation calls back into a
745    /// cell-borrowing function from the Context API, typically `Context::current()`, this will
746    /// result in a `Refcell already borrowed` panic.
747    ///
748    /// As general hygiene, with respect to the [Context] API, it is strongly advised to only use
749    /// simple getters and setters on the `from` and `to` context objects provided to the observer.
750    /// Do not call `Context::current()`, nor try to manually attach or detach context objects from
751    /// an observer.
752    ///
753    /// If you get Refcell-related panics within the [crate::context] module, this is the most
754    /// likely cause.
755    ///
756    /// # Example
757    ///
758    /// ```
759    /// # #[cfg(feature = "experimental_context_observer")]
760    /// # {
761    /// use opentelemetry::Context;
762    /// use opentelemetry::context::{ContextObserver, ObserverContextView};
763    /// use std::any::Any;
764    /// use std::sync::Arc;
765    ///
766    /// // An observer's own view of the context, carrying arbitrary data.
767    /// struct MyView {
768    ///     correlation_id: u64,
769    /// }
770    ///
771    /// impl ObserverContextView for MyView {
772    ///     fn as_any(&self) -> &dyn Any {
773    ///         self
774    ///     }
775    /// }
776    ///
777    /// struct Observer;
778    ///
779    /// # fn do_something(view: &MyView) { }
780    ///
781    /// impl ContextObserver for Observer {
782    ///     fn on_context_enter(&self, from: &Context, to: &Context) {
783    ///         let view = to.observer_view().as_ref().unwrap();
784    ///         do_something(view.as_any().downcast_ref::<MyView>().unwrap());
785    ///     }
786    ///
787    ///     fn on_context_exit(&self, from: &Context, to: &Context) {
788    ///         let view = to
789    ///             .observer_view()
790    ///             .as_ref()
791    ///             .unwrap()
792    ///             .as_any()
793    ///             .downcast_ref::<MyView>()
794    ///             .unwrap();
795    ///         assert_eq!(view.correlation_id, 42);
796    ///     }
797    ///
798    ///     fn make_view(&self, ctx: &Context) -> Option<Arc<dyn ObserverContextView>> {
799    ///         Some(Arc::new(MyView { correlation_id: 42 }))
800    ///     }
801    /// }
802    /// # }
803    /// ```
804    pub trait ContextObserver {
805        /// Called when a context is entered, allowing observers to react to the transition
806        /// from one context (`from`) to another (`to`).
807        fn on_context_enter(&self, from: &Context, to: &Context);
808
809        /// Called when a context is exited, allowing observers to react to the transition
810        /// from one context (`from`) to another (`to`).
811        fn on_context_exit(&self, from: &Context, to: &Context);
812
813        /// Derive a fresh view for a newly created context. The provided `ctx` has an unitialized
814        /// view sets to `None`.
815        ///
816        /// The default implementation returns `None` for observers that don't use the view.
817        fn make_view(&self, _ctx: &Context) -> Option<Arc<dyn ObserverContextView>> {
818            None
819        }
820    }
821
822    static GLOBAL_CONTEXT_OBSERVER: OnceLock<Arc<dyn ContextObserver + Send + Sync>> =
823        OnceLock::new();
824
825    /// A global observer for context transitions.
826    ///
827    /// This struct provides static methods to set and get a global context observer.
828    #[derive(Debug)]
829    pub struct GlobalContextObserver;
830
831    impl GlobalContextObserver {
832        /// Sets the global context observer, logging a warning if it was already set.
833        pub fn set(observer: Arc<dyn ContextObserver + Send + Sync>) {
834            if GLOBAL_CONTEXT_OBSERVER.set(observer).is_err() {
835                otel_warn!(
836                    name: "GlobalContextObserver.SetFailed",
837                    message = "Global context observer was already set. Ignoring new observer."
838                );
839            }
840        }
841
842        /// Returns the global context observer.
843        pub(super) fn get<'a>() -> Option<&'a Arc<dyn ContextObserver + Send + Sync>> {
844            GLOBAL_CONTEXT_OBSERVER.get()
845        }
846    }
847
848    /// A context's observer view of a [Context]. This is an arbitrary data structure. See
849    /// [ContextObserver]'s documentation for examples.
850    #[cfg(feature = "experimental_context_observer")]
851    pub trait ObserverContextView: Any + Send + Sync {
852        /// Returns this view as a `&dyn Any`, enabling downcasting back to the concrete type.
853        ///
854        /// This method is required because this crate's MSRV is below the Rust version (1.86) that
855        /// stabilized trait upcasting. Without it, callers below 1.86 couldn't upcast `&dyn
856        /// ObserverContextView` to `&dyn Any` to perform the downcast themselves.
857        ///
858        /// This is a work-around for the absence of trait upcasting before Rust 1.86.
859        ///
860        /// Implementors should simply return `self`:
861        ///
862        /// ```ignore
863        /// fn as_any(&self) -> &dyn std::any::Any {
864        ///     self
865        /// }
866        /// ```
867        fn as_any(&self) -> &dyn Any;
868    }
869}
870
871#[cfg(test)]
872mod tests {
873    use super::*;
874    use std::time::Duration;
875    use tokio::time::sleep;
876
877    #[derive(Debug, PartialEq)]
878    struct ValueA(u64);
879    #[derive(Debug, PartialEq)]
880    struct ValueB(u64);
881
882    struct ReadCurrentContextOnDrop;
883
884    impl Drop for ReadCurrentContextOnDrop {
885        fn drop(&mut self) {
886            let _ = Context::current();
887        }
888    }
889
890    #[test]
891    fn context_value_drop_can_access_current_context() {
892        let guard = Context::new().with_value(ReadCurrentContextOnDrop).attach();
893
894        drop(guard);
895    }
896
897    #[test]
898    fn out_of_order_context_value_drop_can_access_current_context() {
899        let outer_guard = Context::new().with_value(ReadCurrentContextOnDrop).attach();
900        let inner_guard = Context::new().attach();
901
902        drop(outer_guard);
903        drop(inner_guard);
904    }
905
906    #[test]
907    fn context_immutable() {
908        // start with Current, which should be an empty context
909        let cx = Context::current();
910        assert_eq!(cx.get::<ValueA>(), None);
911        assert_eq!(cx.get::<ValueB>(), None);
912
913        // with_value should return a new context,
914        // leaving the original context unchanged
915        let cx_new = cx.with_value(ValueA(1));
916
917        // cx should be unchanged
918        assert_eq!(cx.get::<ValueA>(), None);
919        assert_eq!(cx.get::<ValueB>(), None);
920
921        // cx_new should contain the new value
922        assert_eq!(cx_new.get::<ValueA>(), Some(&ValueA(1)));
923
924        // cx_new should be unchanged
925        let cx_newer = cx_new.with_value(ValueB(1));
926
927        // Cx and cx_new are unchanged
928        assert_eq!(cx.get::<ValueA>(), None);
929        assert_eq!(cx.get::<ValueB>(), None);
930        assert_eq!(cx_new.get::<ValueA>(), Some(&ValueA(1)));
931        assert_eq!(cx_new.get::<ValueB>(), None);
932
933        // cx_newer should contain both values
934        assert_eq!(cx_newer.get::<ValueA>(), Some(&ValueA(1)));
935        assert_eq!(cx_newer.get::<ValueB>(), Some(&ValueB(1)));
936    }
937
938    #[test]
939    fn nested_contexts() {
940        let _outer_guard = Context::new().with_value(ValueA(1)).attach();
941
942        // Only value `a` is set
943        let current = Context::current();
944        assert_eq!(current.get(), Some(&ValueA(1)));
945        assert_eq!(current.get::<ValueB>(), None);
946
947        {
948            let _inner_guard = Context::current_with_value(ValueB(42)).attach();
949            // Both values are set in inner context
950            let current = Context::current();
951            assert_eq!(current.get(), Some(&ValueA(1)));
952            assert_eq!(current.get(), Some(&ValueB(42)));
953
954            assert!(Context::map_current(|cx| {
955                assert_eq!(cx.get(), Some(&ValueA(1)));
956                assert_eq!(cx.get(), Some(&ValueB(42)));
957                true
958            }));
959        }
960
961        // Resets to only value `a` when inner guard is dropped
962        let current = Context::current();
963        assert_eq!(current.get(), Some(&ValueA(1)));
964        assert_eq!(current.get::<ValueB>(), None);
965
966        assert!(Context::map_current(|cx| {
967            assert_eq!(cx.get(), Some(&ValueA(1)));
968            assert_eq!(cx.get::<ValueB>(), None);
969            true
970        }));
971    }
972
973    #[test]
974    fn overlapping_contexts() {
975        let outer_guard = Context::new().with_value(ValueA(1)).attach();
976
977        // Only value `a` is set
978        let current = Context::current();
979        assert_eq!(current.get(), Some(&ValueA(1)));
980        assert_eq!(current.get::<ValueB>(), None);
981
982        let inner_guard = Context::current_with_value(ValueB(42)).attach();
983        // Both values are set in inner context
984        let current = Context::current();
985        assert_eq!(current.get(), Some(&ValueA(1)));
986        assert_eq!(current.get(), Some(&ValueB(42)));
987
988        assert!(Context::map_current(|cx| {
989            assert_eq!(cx.get(), Some(&ValueA(1)));
990            assert_eq!(cx.get(), Some(&ValueB(42)));
991            true
992        }));
993
994        drop(outer_guard);
995
996        // `inner_guard` is still alive so both `ValueA` and `ValueB` should still be accessible
997        let current = Context::current();
998        assert_eq!(current.get(), Some(&ValueA(1)));
999        assert_eq!(current.get(), Some(&ValueB(42)));
1000
1001        drop(inner_guard);
1002
1003        // Both guards are dropped and neither value should be accessible.
1004        let current = Context::current();
1005        assert_eq!(current.get::<ValueA>(), None);
1006        assert_eq!(current.get::<ValueB>(), None);
1007    }
1008
1009    #[test]
1010    fn too_many_contexts() {
1011        let mut guards: Vec<ContextGuard> = Vec::with_capacity(ContextStack::MAX_POS as usize);
1012        let stack_max_pos = ContextStack::MAX_POS as u64;
1013        // Fill the stack up until the last position
1014        for i in 1..stack_max_pos {
1015            let cx_guard = Context::current().with_value(ValueB(i)).attach();
1016            assert_eq!(Context::current().get(), Some(&ValueB(i)));
1017            assert_eq!(cx_guard.cx_pos, i as u16);
1018            guards.push(cx_guard);
1019        }
1020        // Let's overflow the stack a couple of times
1021        for _ in 0..16 {
1022            let cx_guard = Context::current().with_value(ValueA(1)).attach();
1023            assert_eq!(cx_guard.cx_pos, ContextStack::MAX_POS);
1024            assert_eq!(Context::current().get::<ValueA>(), None);
1025            assert_eq!(Context::current().get(), Some(&ValueB(stack_max_pos - 1)));
1026            guards.push(cx_guard);
1027        }
1028        // Drop the overflow contexts
1029        for _ in 0..16 {
1030            guards.pop();
1031            assert_eq!(Context::current().get::<ValueA>(), None);
1032            assert_eq!(Context::current().get(), Some(&ValueB(stack_max_pos - 1)));
1033        }
1034        // Drop one more so we can add a new one
1035        guards.pop();
1036        assert_eq!(Context::current().get::<ValueA>(), None);
1037        assert_eq!(Context::current().get(), Some(&ValueB(stack_max_pos - 2)));
1038        // Push a new context and see that it works
1039        let cx_guard = Context::current().with_value(ValueA(2)).attach();
1040        assert_eq!(cx_guard.cx_pos, ContextStack::MAX_POS - 1);
1041        assert_eq!(Context::current().get(), Some(&ValueA(2)));
1042        assert_eq!(Context::current().get(), Some(&ValueB(stack_max_pos - 2)));
1043        guards.push(cx_guard);
1044        // Let's overflow the stack a couple of times again
1045        for _ in 0..16 {
1046            let cx_guard = Context::current().with_value(ValueA(1)).attach();
1047            assert_eq!(cx_guard.cx_pos, ContextStack::MAX_POS);
1048            assert_eq!(Context::current().get::<ValueA>(), Some(&ValueA(2)));
1049            assert_eq!(Context::current().get(), Some(&ValueB(stack_max_pos - 2)));
1050            guards.push(cx_guard);
1051        }
1052    }
1053
1054    /// Tests that a new ContextStack is created with the correct initial capacity.
1055    #[test]
1056    fn test_initial_capacity() {
1057        let stack = ContextStack::default();
1058        assert_eq!(stack.stack.capacity(), ContextStack::INITIAL_CAPACITY);
1059    }
1060
1061    /// Tests that map_current_cx correctly accesses the current context.
1062    #[test]
1063    fn test_map_current_cx() {
1064        let mut stack = ContextStack::default();
1065        let test_value = ValueA(42);
1066        stack.current_cx = Context::new().with_value(test_value);
1067
1068        let result = stack.map_current_cx(|cx| {
1069            assert_eq!(cx.get::<ValueA>(), Some(&ValueA(42)));
1070            true
1071        });
1072        assert!(result);
1073    }
1074
1075    /// Tests popping contexts in non-sequential order.
1076    #[test]
1077    fn test_pop_id_out_of_order() {
1078        let mut stack = ContextStack::default();
1079
1080        // Push three contexts
1081        let cx1 = Context::new().with_value(ValueA(1));
1082        let cx2 = Context::new().with_value(ValueA(2));
1083        let cx3 = Context::new().with_value(ValueA(3));
1084
1085        let id1 = stack.push(cx1);
1086        let id2 = stack.push(cx2);
1087        let id3 = stack.push(cx3);
1088
1089        // Pop middle context first - should not affect current context
1090        stack.pop_id(id2);
1091        assert_eq!(stack.current_cx.get::<ValueA>(), Some(&ValueA(3)));
1092        assert_eq!(stack.stack.len(), 3); // Length unchanged for middle pops
1093
1094        // Pop last context - should restore previous valid context
1095        stack.pop_id(id3);
1096        assert_eq!(stack.current_cx.get::<ValueA>(), Some(&ValueA(1)));
1097        assert_eq!(stack.stack.len(), 1);
1098
1099        // Pop first context - should restore to empty state
1100        stack.pop_id(id1);
1101        assert_eq!(stack.current_cx.get::<ValueA>(), None);
1102        assert_eq!(stack.stack.len(), 0);
1103    }
1104
1105    /// Tests edge cases in context stack operations. IRL these should log
1106    /// warnings, and definitely not panic.
1107    #[test]
1108    fn test_pop_id_edge_cases() {
1109        let mut stack = ContextStack::default();
1110
1111        // Test popping BASE_POS - should be no-op
1112        stack.pop_id(ContextStack::BASE_POS);
1113        assert_eq!(stack.stack.len(), 0);
1114
1115        // Test popping MAX_POS - should be no-op
1116        stack.pop_id(ContextStack::MAX_POS);
1117        assert_eq!(stack.stack.len(), 0);
1118
1119        // Test popping invalid position - should be no-op
1120        stack.pop_id(1000);
1121        assert_eq!(stack.stack.len(), 0);
1122
1123        // Test popping from empty stack - should be safe
1124        stack.pop_id(1);
1125        assert_eq!(stack.stack.len(), 0);
1126    }
1127
1128    /// Tests stack behavior when reaching maximum capacity.
1129    /// Once we push beyond this point, we should end up with a context
1130    /// that points _somewhere_, but mutating it should not affect the current
1131    /// active context.
1132    #[test]
1133    fn test_push_overflow() {
1134        let mut stack = ContextStack::default();
1135        let max_pos = ContextStack::MAX_POS as usize;
1136
1137        // Fill stack up to max position
1138        for i in 0..max_pos {
1139            let cx = Context::new().with_value(ValueA(i as u64));
1140            let id = stack.push(cx);
1141            assert_eq!(id, (i + 1) as u16);
1142        }
1143
1144        // Try to push beyond capacity
1145        let cx = Context::new().with_value(ValueA(max_pos as u64));
1146        let id = stack.push(cx);
1147        assert_eq!(id, ContextStack::MAX_POS);
1148
1149        // Verify current context remains unchanged after overflow
1150        assert_eq!(
1151            stack.current_cx.get::<ValueA>(),
1152            Some(&ValueA((max_pos - 2) as u64))
1153        );
1154    }
1155
1156    /// Tests that:
1157    /// 1. Parent context values are properly propagated to async operations
1158    /// 2. Values added during async operations do not affect parent context
1159    #[tokio::test]
1160    async fn test_async_context_propagation() {
1161        // A nested async operation we'll use to test propagation
1162        async fn nested_operation() {
1163            // Verify we can see the parent context's value
1164            assert_eq!(
1165                Context::current().get::<ValueA>(),
1166                Some(&ValueA(42)),
1167                "Parent context value should be available in async operation"
1168            );
1169
1170            // Create new context
1171            let cx_with_both = Context::current()
1172                .with_value(ValueA(43)) // override ValueA
1173                .with_value(ValueB(24)); // Add new ValueB
1174
1175            // Run nested async operation with both values
1176            async {
1177                // Verify both values are available
1178                assert_eq!(
1179                    Context::current().get::<ValueA>(),
1180                    Some(&ValueA(43)),
1181                    "Parent value should still be available after adding new value"
1182                );
1183                assert_eq!(
1184                    Context::current().get::<ValueB>(),
1185                    Some(&ValueB(24)),
1186                    "New value should be available in async operation"
1187                );
1188
1189                // Do some async work to simulate real-world scenario
1190                sleep(Duration::from_millis(10)).await;
1191
1192                // Values should still be available after async work
1193                assert_eq!(
1194                    Context::current().get::<ValueA>(),
1195                    Some(&ValueA(43)),
1196                    "Parent value should persist across await points"
1197                );
1198                assert_eq!(
1199                    Context::current().get::<ValueB>(),
1200                    Some(&ValueB(24)),
1201                    "New value should persist across await points"
1202                );
1203            }
1204            .with_context(cx_with_both)
1205            .await;
1206        }
1207
1208        // Set up initial context with ValueA
1209        let parent_cx = Context::new().with_value(ValueA(42));
1210
1211        // Create and run async operation with the parent context explicitly propagated
1212        nested_operation().with_context(parent_cx.clone()).await;
1213
1214        // After async operation completes:
1215        // 1. Parent context should be unchanged
1216        assert_eq!(
1217            parent_cx.get::<ValueA>(),
1218            Some(&ValueA(42)),
1219            "Parent context should be unchanged"
1220        );
1221        assert_eq!(
1222            parent_cx.get::<ValueB>(),
1223            None,
1224            "Parent context should not see values added in async operation"
1225        );
1226
1227        // 2. Current context should be back to default
1228        assert_eq!(
1229            Context::current().get::<ValueA>(),
1230            None,
1231            "Current context should be back to default"
1232        );
1233        assert_eq!(
1234            Context::current().get::<ValueB>(),
1235            None,
1236            "Current context should not have async operation's values"
1237        );
1238    }
1239
1240    ///
1241    /// Tests that unnatural parent->child relationships in nested async
1242    /// operations behave properly.
1243    ///
1244    #[tokio::test]
1245    async fn test_out_of_order_context_detachment_futures() {
1246        // This function returns a future, but doesn't await it
1247        // It will complete before the future that it creates.
1248        async fn create_a_future() -> impl std::future::Future<Output = ()> {
1249            // Create a future that will do some work, referencing our current
1250            // context, but don't await it.
1251            async {
1252                assert_eq!(Context::current().get::<ValueA>(), Some(&ValueA(42)));
1253
1254                // Longer work
1255                sleep(Duration::from_millis(50)).await;
1256            }
1257            .with_context(Context::current())
1258        }
1259
1260        // Create our base context
1261        let parent_cx = Context::new().with_value(ValueA(42));
1262
1263        // await our nested function, which will create and detach a context
1264        let future = create_a_future().with_context(parent_cx).await;
1265
1266        // Execute the future. The future that created it is long gone, but this shouldn't
1267        // cause issues.
1268        let _a = future.await;
1269
1270        // Nothing terrible (e.g., panics!) should happen, and we should definitely not have any
1271        // values attached to our current context that were set in the nested operations.
1272        assert_eq!(Context::current().get::<ValueA>(), None);
1273        assert_eq!(Context::current().get::<ValueB>(), None);
1274    }
1275
1276    #[test]
1277    fn test_is_telemetry_suppressed() {
1278        // Default context has suppression disabled
1279        let cx = Context::new();
1280        assert!(!cx.is_telemetry_suppressed());
1281
1282        // With suppression enabled
1283        let suppressed = cx.with_telemetry_suppressed();
1284        assert!(suppressed.is_telemetry_suppressed());
1285    }
1286
1287    #[test]
1288    fn test_with_telemetry_suppressed() {
1289        // Start with a normal context
1290        let cx = Context::new();
1291        assert!(!cx.is_telemetry_suppressed());
1292
1293        // Create a suppressed context
1294        let suppressed = cx.with_telemetry_suppressed();
1295
1296        // Original should remain unchanged
1297        assert!(!cx.is_telemetry_suppressed());
1298
1299        // New context should be suppressed
1300        assert!(suppressed.is_telemetry_suppressed());
1301
1302        // Test with values to ensure they're preserved
1303        let cx_with_value = cx.with_value(ValueA(42));
1304        let suppressed_with_value = cx_with_value.with_telemetry_suppressed();
1305
1306        assert!(!cx_with_value.is_telemetry_suppressed());
1307        assert!(suppressed_with_value.is_telemetry_suppressed());
1308        assert_eq!(suppressed_with_value.get::<ValueA>(), Some(&ValueA(42)));
1309    }
1310
1311    #[test]
1312    fn test_enter_telemetry_suppressed_scope() {
1313        // Ensure we start with a clean context
1314        let _reset_guard = Context::new().attach();
1315
1316        // Default context should not be suppressed
1317        assert!(!Context::is_current_telemetry_suppressed());
1318
1319        // Add an entry to the current context
1320        let cx_with_value = Context::current().with_value(ValueA(42));
1321        let _guard_with_value = cx_with_value.attach();
1322
1323        // Verify the entry is present and context is not suppressed
1324        assert_eq!(Context::current().get::<ValueA>(), Some(&ValueA(42)));
1325        assert!(!Context::is_current_telemetry_suppressed());
1326
1327        // Enter a suppressed scope
1328        {
1329            let _guard = Context::enter_telemetry_suppressed_scope();
1330
1331            // Verify suppression is active and the entry is still present
1332            assert!(Context::is_current_telemetry_suppressed());
1333            assert!(Context::current().is_telemetry_suppressed());
1334            assert_eq!(Context::current().get::<ValueA>(), Some(&ValueA(42)));
1335        }
1336
1337        // After guard is dropped, should be back to unsuppressed and entry should still be present
1338        assert!(!Context::is_current_telemetry_suppressed());
1339        assert!(!Context::current().is_telemetry_suppressed());
1340        assert_eq!(Context::current().get::<ValueA>(), Some(&ValueA(42)));
1341    }
1342
1343    #[test]
1344    fn test_nested_suppression_scopes() {
1345        // Ensure we start with a clean context
1346        let _reset_guard = Context::new().attach();
1347
1348        // Default context should not be suppressed
1349        assert!(!Context::is_current_telemetry_suppressed());
1350
1351        // First level suppression
1352        {
1353            let _outer = Context::enter_telemetry_suppressed_scope();
1354            assert!(Context::is_current_telemetry_suppressed());
1355
1356            // Second level. This component is unaware of Suppression,
1357            // and just attaches a new context. Since it is from current,
1358            // it'll already have suppression enabled.
1359            {
1360                let _inner = Context::current().with_value(ValueA(1)).attach();
1361                assert!(Context::is_current_telemetry_suppressed());
1362                assert_eq!(Context::current().get::<ValueA>(), Some(&ValueA(1)));
1363            }
1364
1365            // Another scenario. This component is unaware of Suppression,
1366            // and just attaches a new context, not from Current. Since it is
1367            // not from current it will not have suppression enabled.
1368            {
1369                let _inner = Context::new().with_value(ValueA(1)).attach();
1370                assert!(!Context::is_current_telemetry_suppressed());
1371                assert_eq!(Context::current().get::<ValueA>(), Some(&ValueA(1)));
1372            }
1373
1374            // Still suppressed after inner scope
1375            assert!(Context::is_current_telemetry_suppressed());
1376        }
1377
1378        // Back to unsuppressed
1379        assert!(!Context::is_current_telemetry_suppressed());
1380    }
1381
1382    #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
1383    async fn test_async_suppression() {
1384        async fn nested_operation() {
1385            assert!(Context::is_current_telemetry_suppressed());
1386
1387            let cx_with_additional_value = Context::current().with_value(ValueB(24));
1388
1389            async {
1390                assert_eq!(
1391                    Context::current().get::<ValueB>(),
1392                    Some(&ValueB(24)),
1393                    "Parent value should still be available after adding new value"
1394                );
1395                assert!(Context::is_current_telemetry_suppressed());
1396
1397                // Do some async work to simulate real-world scenario
1398                sleep(Duration::from_millis(10)).await;
1399
1400                // Values should still be available after async work
1401                assert_eq!(
1402                    Context::current().get::<ValueB>(),
1403                    Some(&ValueB(24)),
1404                    "Parent value should still be available after adding new value"
1405                );
1406                assert!(Context::is_current_telemetry_suppressed());
1407            }
1408            .with_context(cx_with_additional_value)
1409            .await;
1410        }
1411
1412        // Set up suppressed context, but don't attach it to current
1413        let suppressed_parent = Context::new().with_telemetry_suppressed();
1414        // Current should not be suppressed as we haven't attached it
1415        assert!(!Context::is_current_telemetry_suppressed());
1416
1417        // Create and run async operation with the suppressed context explicitly propagated
1418        nested_operation()
1419            .with_context(suppressed_parent.clone())
1420            .await;
1421
1422        // After async operation completes:
1423        // Suppression should be active
1424        assert!(suppressed_parent.is_telemetry_suppressed());
1425
1426        // Current should still be not suppressed
1427        assert!(!Context::is_current_telemetry_suppressed());
1428    }
1429}
1430
1431#[cfg(all(test, feature = "experimental_context_observer"))]
1432mod observer_tests {
1433    use super::*;
1434    use std::cell::{Cell, RefCell};
1435    use std::sync::Arc;
1436
1437    #[derive(Debug, PartialEq)]
1438    struct V(u64);
1439
1440    #[derive(Debug, PartialEq, Clone)]
1441    enum Event {
1442        Enter { from: Option<u64>, to: Option<u64> },
1443        Exit { from: Option<u64>, to: Option<u64> },
1444    }
1445
1446    thread_local! {
1447        static EVENTS: RefCell<Vec<Event>> = const { RefCell::new(Vec::new()) };
1448    }
1449
1450    fn value_of(cx: &Context) -> Option<u64> {
1451        cx.get::<V>().map(|v| v.0)
1452    }
1453
1454    // When enabled, on each transition it records the event into a thread-local buffer, and on
1455    // enter it also installs an observer view on the entered context and bumps the thread-local
1456    // `VIEWS_CREATED` counter. See `observer_installs_and_reuses_view`.
1457    //
1458    // The observer is a process-global singleton, so its callbacks fire for context activity on
1459    // *every* thread across the whole test binary once installed. To keep that cheap and isolated,
1460    // all of its work is gated behind the per-thread `ENABLE_TEST_OBSERVER` switch: threads that
1461    // didn't opt in (via `setup()`) get an inert observer.
1462    struct RecordingObserver;
1463
1464    impl ContextObserver for RecordingObserver {
1465        fn on_context_enter(&self, from: &Context, to: &Context) {
1466            if !ENABLE_TEST_OBSERVER.with(Cell::get) {
1467                return;
1468            }
1469
1470            EVENTS.with(|e| {
1471                e.borrow_mut().push(Event::Enter {
1472                    from: value_of(from),
1473                    to: value_of(to),
1474                })
1475            });
1476        }
1477
1478        fn on_context_exit(&self, from: &Context, to: &Context) {
1479            if !ENABLE_TEST_OBSERVER.with(Cell::get) {
1480                return;
1481            }
1482
1483            EVENTS.with(|e| {
1484                e.borrow_mut().push(Event::Exit {
1485                    from: value_of(from),
1486                    to: value_of(to),
1487                })
1488            });
1489        }
1490
1491        // On context enter, install the observer's view if this context doesn't have one yet, or reuse
1492        // the existing one otherwise. The view is the context's `V` value (or 0 for a value-less
1493        // context).
1494        fn make_view(&self, cx: &Context) -> Option<Arc<dyn ObserverContextView>> {
1495            let correlation_id = value_of(cx).unwrap_or(0);
1496            VIEWS_CREATED.with(|c| c.set(c.get() + 1));
1497            Some(Arc::new(MyView { correlation_id }))
1498        }
1499    }
1500
1501    thread_local! {
1502        // Master switch for the observer per thread. Enabled for the duration of a test that cares,
1503        // and left false everywhere else, so we don't slow down tests unrelated to the observer.
1504        // See `setup`.
1505        static ENABLE_TEST_OBSERVER: Cell<bool> = const { Cell::new(false) };
1506        // Counts how many distinct views the observer created on the current thread.
1507        static VIEWS_CREATED: Cell<u64> = const { Cell::new(0) };
1508    }
1509
1510    // Recovers the correlation id stored in a context's observer view, if any.
1511    fn view_id(cx: &Context) -> Option<u64> {
1512        cx.observer_view()
1513            .as_ref()
1514            .and_then(|v| v.as_any().downcast_ref::<MyView>())
1515            .map(|v| v.correlation_id)
1516    }
1517
1518    // Holds the base context guard and disables the observer on this thread when dropped. The
1519    // struct's own `Drop` runs before its fields, so the flag is cleared before the base context is
1520    // popped, which is thus not observed (symmetrically to `setup`, which installs the base context
1521    // before registering/enabling the observer).
1522    struct ObserverTestGuard {
1523        _cx: ContextGuard,
1524    }
1525
1526    impl Drop for ObserverTestGuard {
1527        fn drop(&mut self) {
1528            ENABLE_TEST_OBSERVER.with(|e| e.set(false));
1529        }
1530    }
1531
1532    // Enables the observer on this thread, establishes a clean base context, and clears any
1533    // previously recorded events, so the assertions below only see transitions this test
1534    // triggers. Dropping the returned guard disables the observer again.
1535    fn setup_observer() -> ObserverTestGuard {
1536        // Idempotent across tests: only the first call installs the observer, but every test
1537        // uses the same `RecordingObserver`, so a lost race is harmless.
1538        GlobalContextObserver::set(Arc::new(RecordingObserver));
1539        ENABLE_TEST_OBSERVER.with(|e| e.set(true));
1540        let guard = Context::new().attach();
1541        EVENTS.with(|e| e.borrow_mut().clear());
1542        ObserverTestGuard { _cx: guard }
1543    }
1544
1545    #[test]
1546    fn global_observer_records_enter_and_exit() {
1547        let _clean = setup_observer();
1548
1549        {
1550            let _g = Context::current().with_value(V(1)).attach();
1551            // Entering fires immediately: from the clean base (no value) to `V(1)`.
1552            EVENTS.with(|e| {
1553                assert_eq!(
1554                    *e.borrow(),
1555                    vec![Event::Enter {
1556                        from: None,
1557                        to: Some(1)
1558                    }]
1559                )
1560            });
1561        }
1562
1563        // Dropping the (context) guard restores the base and fires the matching exit event.
1564        EVENTS.with(|e| {
1565            assert_eq!(
1566                *e.borrow(),
1567                vec![
1568                    Event::Enter {
1569                        from: None,
1570                        to: Some(1)
1571                    },
1572                    Event::Exit {
1573                        from: Some(1),
1574                        to: None
1575                    }
1576                ]
1577            )
1578        });
1579    }
1580
1581    #[test]
1582    fn global_observer_records_nested_transitions() {
1583        let _clean = setup_observer();
1584
1585        let g1 = Context::current().with_value(V(1)).attach();
1586        let g2 = Context::current().with_value(V(2)).attach();
1587        drop(g2);
1588        drop(g1);
1589
1590        EVENTS.with(|e| {
1591            assert_eq!(
1592                *e.borrow(),
1593                vec![
1594                    Event::Enter {
1595                        from: None,
1596                        to: Some(1)
1597                    },
1598                    Event::Enter {
1599                        from: Some(1),
1600                        to: Some(2)
1601                    },
1602                    Event::Exit {
1603                        from: Some(2),
1604                        to: Some(1)
1605                    },
1606                    Event::Exit {
1607                        from: Some(1),
1608                        to: None
1609                    },
1610                ]
1611            )
1612        });
1613    }
1614
1615    // An observer view carrying arbitrary data, unrelated to the internal `Context`
1616    // representation.
1617    struct MyView {
1618        correlation_id: u64,
1619    }
1620
1621    impl ObserverContextView for MyView {
1622        fn as_any(&self) -> &dyn Any {
1623            self
1624        }
1625    }
1626
1627    // A realistic use of the observer view: the global observer installs its own view on each
1628    // context the first time that context is entered, and reuses it on re-entry. The
1629    // thread-local counter checks that exactly one view is created per distinct context.
1630    #[test]
1631    fn observer_installs_and_reuses_view() {
1632        let _clean = setup_observer();
1633
1634        // Reset the counter for the work below.
1635        VIEWS_CREATED.with(|c| c.set(0));
1636
1637        // Entering two distinct contexts installs a fresh view for each, derived from its value.
1638        let g1 = Context::current().with_value(V(1)).attach();
1639        assert_eq!(view_id(&Context::current()), Some(1));
1640
1641        let g2 = Context::current().with_value(V(2)).attach();
1642        assert_eq!(view_id(&Context::current()), Some(2));
1643
1644        assert_eq!(VIEWS_CREATED.with(Cell::get), 2);
1645
1646        // Re-entering an already-observed context must reuse its view, not create a new one.
1647        // `Context::current()` clones the current context, carrying along its already-set view.
1648        let observed = Context::current();
1649        let g3 = observed.attach();
1650
1651        assert_eq!(view_id(&Context::current()), Some(2));
1652        assert_eq!(
1653            VIEWS_CREATED.with(Cell::get),
1654            2,
1655            "re-entering an observed context must not create a new view"
1656        );
1657
1658        drop(g3);
1659        drop(g2);
1660        drop(g1);
1661    }
1662}