opentelemetry_spanprocessor_any/
context.rs

1use std::any::{Any, TypeId};
2use std::cell::RefCell;
3use std::collections::HashMap;
4use std::fmt;
5use std::hash::{BuildHasherDefault, Hasher};
6use std::marker::PhantomData;
7use std::sync::Arc;
8
9thread_local! {
10    static CURRENT_CONTEXT: RefCell<Context> = RefCell::new(Context::default());
11    static DEFAULT_CONTEXT: Context = Context::default();
12}
13
14/// An execution-scoped collection of values.
15///
16/// A [`Context`] is a propagation mechanism which carries execution-scoped
17/// values across API boundaries and between logically associated execution
18/// units. Cross-cutting concerns access their data in-process using the same
19/// shared context object.
20///
21/// [`Context`]s are immutable, and their write operations result in the creation
22/// of a new context containing the original values and the new specified values.
23///
24/// ## Context state
25///
26/// Concerns can create and retrieve their local state in the current execution
27/// state represented by a context through the [`get`] and [`with_value`]
28/// methods. It is recommended to use application-specific types when storing new
29/// context values to avoid unintentionally overwriting existing state.
30///
31/// ## Managing the current context
32///
33/// Contexts can be associated with the caller's current execution unit on a
34/// given thread via the [`attach`] method, and previous contexts can be restored
35/// by dropping the returned [`ContextGuard`]. Context can be nested, and will
36/// restore their parent outer context when detached on drop. To access the
37/// values of the context, a snapshot can be created via the [`Context::current`]
38/// method.
39///
40/// [`Context::current`]: Context::current()
41/// [`get`]: Context::get()
42/// [`with_value`]: Context::with_value()
43/// [`attach`]: Context::attach()
44///
45/// # Examples
46///
47/// ```
48/// use opentelemetry::Context;
49///
50/// // Application-specific `a` and `b` values
51/// #[derive(Debug, PartialEq)]
52/// struct ValueA(&'static str);
53/// #[derive(Debug, PartialEq)]
54/// struct ValueB(u64);
55///
56/// let _outer_guard = Context::new().with_value(ValueA("a")).attach();
57///
58/// // Only value a has been set
59/// let current = Context::current();
60/// assert_eq!(current.get::<ValueA>(), Some(&ValueA("a")));
61/// assert_eq!(current.get::<ValueB>(), None);
62///
63/// {
64///     let _inner_guard = Context::current_with_value(ValueB(42)).attach();
65///     // Both values are set in inner context
66///     let current = Context::current();
67///     assert_eq!(current.get::<ValueA>(), Some(&ValueA("a")));
68///     assert_eq!(current.get::<ValueB>(), Some(&ValueB(42)));
69/// }
70///
71/// // Resets to only the `a` value when inner guard is dropped
72/// let current = Context::current();
73/// assert_eq!(current.get::<ValueA>(), Some(&ValueA("a")));
74/// assert_eq!(current.get::<ValueB>(), None);
75/// ```
76#[derive(Clone, Default)]
77pub struct Context {
78    entries: HashMap<TypeId, Arc<dyn Any + Sync + Send>, BuildHasherDefault<IdHasher>>,
79}
80
81impl Context {
82    /// Creates an empty `Context`.
83    ///
84    /// The context is initially created with a capacity of 0, so it will not
85    /// allocate. Use [`with_value`] to create a new context that has entries.
86    ///
87    /// [`with_value`]: Context::with_value()
88    pub fn new() -> Self {
89        Context::default()
90    }
91
92    /// Returns an immutable snapshot of the current thread's context.
93    ///
94    /// # Examples
95    ///
96    /// ```
97    /// use opentelemetry::Context;
98    ///
99    /// #[derive(Debug, PartialEq)]
100    /// struct ValueA(&'static str);
101    ///
102    /// fn do_work() {
103    ///     assert_eq!(Context::current().get(), Some(&ValueA("a")));
104    /// }
105    ///
106    /// let _guard = Context::new().with_value(ValueA("a")).attach();
107    /// do_work()
108    /// ```
109    pub fn current() -> Self {
110        get_current(|cx| cx.clone())
111    }
112
113    /// Returns a clone of the current thread's context with the given value.
114    ///
115    /// This is a more efficient form of `Context::current().with_value(value)`
116    /// as it avoids the intermediate context clone.
117    ///
118    /// # Examples
119    ///
120    /// ```
121    /// use opentelemetry::Context;
122    ///
123    /// // Given some value types defined in your application
124    /// #[derive(Debug, PartialEq)]
125    /// struct ValueA(&'static str);
126    /// #[derive(Debug, PartialEq)]
127    /// struct ValueB(u64);
128    ///
129    /// // You can create and attach context with the first value set to "a"
130    /// let _guard = Context::new().with_value(ValueA("a")).attach();
131    ///
132    /// // And create another context based on the fist with a new value
133    /// let all_current_and_b = Context::current_with_value(ValueB(42));
134    ///
135    /// // The second context now contains all the current values and the addition
136    /// assert_eq!(all_current_and_b.get::<ValueA>(), Some(&ValueA("a")));
137    /// assert_eq!(all_current_and_b.get::<ValueB>(), Some(&ValueB(42)));
138    /// ```
139    pub fn current_with_value<T: 'static + Send + Sync>(value: T) -> Self {
140        let mut new_context = Context::current();
141        new_context
142            .entries
143            .insert(TypeId::of::<T>(), Arc::new(value));
144
145        new_context
146    }
147
148    /// Returns a reference to the entry for the corresponding value type.
149    ///
150    /// # Examples
151    ///
152    /// ```
153    /// use opentelemetry::Context;
154    ///
155    /// // Given some value types defined in your application
156    /// #[derive(Debug, PartialEq)]
157    /// struct ValueA(&'static str);
158    /// #[derive(Debug, PartialEq)]
159    /// struct MyUser();
160    ///
161    /// let cx = Context::new().with_value(ValueA("a"));
162    ///
163    /// // Values can be queried by type
164    /// assert_eq!(cx.get::<ValueA>(), Some(&ValueA("a")));
165    ///
166    /// // And return none if not yet set
167    /// assert_eq!(cx.get::<MyUser>(), None);
168    /// ```
169    pub fn get<T: 'static>(&self) -> Option<&T> {
170        self.entries
171            .get(&TypeId::of::<T>())
172            .and_then(|rc| (&*rc).downcast_ref())
173    }
174
175    /// Returns a copy of the context with the new value included.
176    ///
177    /// # Examples
178    ///
179    /// ```
180    /// use opentelemetry::Context;
181    ///
182    /// // Given some value types defined in your application
183    /// #[derive(Debug, PartialEq)]
184    /// struct ValueA(&'static str);
185    /// #[derive(Debug, PartialEq)]
186    /// struct ValueB(u64);
187    ///
188    /// // You can create a context with the first value set to "a"
189    /// let cx_with_a = Context::new().with_value(ValueA("a"));
190    ///
191    /// // And create another context based on the fist with a new value
192    /// let cx_with_a_and_b = cx_with_a.with_value(ValueB(42));
193    ///
194    /// // The first context is still available and unmodified
195    /// assert_eq!(cx_with_a.get::<ValueA>(), Some(&ValueA("a")));
196    /// assert_eq!(cx_with_a.get::<ValueB>(), None);
197    ///
198    /// // The second context now contains both values
199    /// assert_eq!(cx_with_a_and_b.get::<ValueA>(), Some(&ValueA("a")));
200    /// assert_eq!(cx_with_a_and_b.get::<ValueB>(), Some(&ValueB(42)));
201    /// ```
202    pub fn with_value<T: 'static + Send + Sync>(&self, value: T) -> Self {
203        let mut new_context = self.clone();
204        new_context
205            .entries
206            .insert(TypeId::of::<T>(), Arc::new(value));
207
208        new_context
209    }
210
211    /// Replaces the current context on this thread with this context.
212    ///
213    /// Dropping the returned [`ContextGuard`] will reset the current context to the
214    /// previous value.
215    ///
216    ///
217    /// # Examples
218    ///
219    /// ```
220    /// use opentelemetry::Context;
221    ///
222    /// #[derive(Debug, PartialEq)]
223    /// struct ValueA(&'static str);
224    ///
225    /// let my_cx = Context::new().with_value(ValueA("a"));
226    ///
227    /// // Set the current thread context
228    /// let cx_guard = my_cx.attach();
229    /// assert_eq!(Context::current().get::<ValueA>(), Some(&ValueA("a")));
230    ///
231    /// // Drop the guard to restore the previous context
232    /// drop(cx_guard);
233    /// assert_eq!(Context::current().get::<ValueA>(), None);
234    /// ```
235    ///
236    /// Guards do not need to be explicitly dropped:
237    ///
238    /// ```
239    /// use opentelemetry::Context;
240    ///
241    /// #[derive(Debug, PartialEq)]
242    /// struct ValueA(&'static str);
243    ///
244    /// fn my_function() -> String {
245    ///     // attach a context the duration of this function.
246    ///     let my_cx = Context::new().with_value(ValueA("a"));
247    ///     // NOTE: a variable name after the underscore is **required** or rust
248    ///     // will drop the guard, restoring the previous context _immediately_.
249    ///     let _guard = my_cx.attach();
250    ///
251    ///     // anything happening in functions we call can still access my_cx...
252    ///     my_other_function();
253    ///
254    ///     // returning from the function drops the guard, exiting the span.
255    ///     return "Hello world".to_owned();
256    /// }
257    ///
258    /// fn my_other_function() {
259    ///     // ...
260    /// }
261    /// ```
262    /// Sub-scopes may be created to limit the duration for which the span is
263    /// entered:
264    ///
265    /// ```
266    /// use opentelemetry::Context;
267    ///
268    /// #[derive(Debug, PartialEq)]
269    /// struct ValueA(&'static str);
270    ///
271    /// let my_cx = Context::new().with_value(ValueA("a"));
272    ///
273    /// {
274    ///     let _guard = my_cx.attach();
275    ///
276    ///     // the current context can access variables in
277    ///     assert_eq!(Context::current().get::<ValueA>(), Some(&ValueA("a")));
278    ///
279    ///     // exiting the scope drops the guard, detaching the context.
280    /// }
281    ///
282    /// // this is back in the default empty context
283    /// assert_eq!(Context::current().get::<ValueA>(), None);
284    /// ```
285    pub fn attach(self) -> ContextGuard {
286        let previous_cx = CURRENT_CONTEXT
287            .try_with(|current| current.replace(self))
288            .ok();
289
290        ContextGuard {
291            previous_cx,
292            _marker: PhantomData,
293        }
294    }
295}
296
297impl fmt::Debug for Context {
298    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
299        f.debug_struct("Context")
300            .field("entries", &self.entries.len())
301            .finish()
302    }
303}
304
305/// A guard that resets the current context to the prior context when dropped.
306#[allow(missing_debug_implementations)]
307pub struct ContextGuard {
308    previous_cx: Option<Context>,
309    // ensure this type is !Send as it relies on thread locals
310    _marker: PhantomData<*const ()>,
311}
312
313impl Drop for ContextGuard {
314    fn drop(&mut self) {
315        if let Some(previous_cx) = self.previous_cx.take() {
316            let _ = CURRENT_CONTEXT.try_with(|current| current.replace(previous_cx));
317        }
318    }
319}
320
321/// Executes a closure with a reference to this thread's current context.
322///
323/// Note: This function will panic if you attempt to attach another context
324/// while the context is still borrowed.
325fn get_current<F: FnMut(&Context) -> T, T>(mut f: F) -> T {
326    CURRENT_CONTEXT
327        .try_with(|cx| f(&*cx.borrow()))
328        .unwrap_or_else(|_| DEFAULT_CONTEXT.with(|cx| f(&*cx)))
329}
330
331/// With TypeIds as keys, there's no need to hash them. They are already hashes
332/// themselves, coming from the compiler. The IdHasher holds the u64 of
333/// the TypeId, and then returns it, instead of doing any bit fiddling.
334#[derive(Clone, Default, Debug)]
335struct IdHasher(u64);
336
337impl Hasher for IdHasher {
338    fn write(&mut self, _: &[u8]) {
339        unreachable!("TypeId calls write_u64");
340    }
341
342    #[inline]
343    fn write_u64(&mut self, id: u64) {
344        self.0 = id;
345    }
346
347    #[inline]
348    fn finish(&self) -> u64 {
349        self.0
350    }
351}
352
353#[cfg(test)]
354mod tests {
355    use super::*;
356
357    #[test]
358    fn nested_contexts() {
359        #[derive(Debug, PartialEq)]
360        struct ValueA(&'static str);
361        #[derive(Debug, PartialEq)]
362        struct ValueB(u64);
363        let _outer_guard = Context::new().with_value(ValueA("a")).attach();
364
365        // Only value `a` is set
366        let current = Context::current();
367        assert_eq!(current.get(), Some(&ValueA("a")));
368        assert_eq!(current.get::<ValueB>(), None);
369
370        {
371            let _inner_guard = Context::current_with_value(ValueB(42)).attach();
372            // Both values are set in inner context
373            let current = Context::current();
374            assert_eq!(current.get(), Some(&ValueA("a")));
375            assert_eq!(current.get(), Some(&ValueB(42)));
376        }
377
378        // Resets to only value `a` when inner guard is dropped
379        let current = Context::current();
380        assert_eq!(current.get(), Some(&ValueA("a")));
381        assert_eq!(current.get::<ValueB>(), None);
382    }
383}