opentelemetry/
context.rs

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