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}