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}