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 let mut dbg = f.debug_struct("Context");
331 let mut entries = self.entries.len();
332 #[cfg(feature = "trace")]
333 {
334 if let Some(span) = &self.span {
335 dbg.field("span", &span.span_context());
336 entries += 1;
337 } else {
338 dbg.field("span", &"None");
339 }
340 }
341
342 dbg.field("entries", &entries).finish()
343 }
344}
345
346/// A guard that resets the current context to the prior context when dropped.
347#[allow(missing_debug_implementations)]
348pub struct ContextGuard {
349 previous_cx: Option<Context>,
350 // ensure this type is !Send as it relies on thread locals
351 _marker: PhantomData<*const ()>,
352}
353
354impl Drop for ContextGuard {
355 fn drop(&mut self) {
356 if let Some(previous_cx) = self.previous_cx.take() {
357 let _ = CURRENT_CONTEXT.try_with(|current| current.replace(previous_cx));
358 }
359 }
360}
361
362/// With TypeIds as keys, there's no need to hash them. They are already hashes
363/// themselves, coming from the compiler. The IdHasher holds the u64 of
364/// the TypeId, and then returns it, instead of doing any bit fiddling.
365#[derive(Clone, Default, Debug)]
366struct IdHasher(u64);
367
368impl Hasher for IdHasher {
369 fn write(&mut self, _: &[u8]) {
370 unreachable!("TypeId calls write_u64");
371 }
372
373 #[inline]
374 fn write_u64(&mut self, id: u64) {
375 self.0 = id;
376 }
377
378 #[inline]
379 fn finish(&self) -> u64 {
380 self.0
381 }
382}
383
384#[cfg(test)]
385mod tests {
386 use super::*;
387
388 #[test]
389 fn nested_contexts() {
390 #[derive(Debug, PartialEq)]
391 struct ValueA(&'static str);
392 #[derive(Debug, PartialEq)]
393 struct ValueB(u64);
394 let _outer_guard = Context::new().with_value(ValueA("a")).attach();
395
396 // Only value `a` is set
397 let current = Context::current();
398 assert_eq!(current.get(), Some(&ValueA("a")));
399 assert_eq!(current.get::<ValueB>(), None);
400
401 {
402 let _inner_guard = Context::current_with_value(ValueB(42)).attach();
403 // Both values are set in inner context
404 let current = Context::current();
405 assert_eq!(current.get(), Some(&ValueA("a")));
406 assert_eq!(current.get(), Some(&ValueB(42)));
407
408 assert!(Context::map_current(|cx| {
409 assert_eq!(cx.get(), Some(&ValueA("a")));
410 assert_eq!(cx.get(), Some(&ValueB(42)));
411 true
412 }));
413 }
414
415 // Resets to only value `a` when inner guard is dropped
416 let current = Context::current();
417 assert_eq!(current.get(), Some(&ValueA("a")));
418 assert_eq!(current.get::<ValueB>(), None);
419
420 assert!(Context::map_current(|cx| {
421 assert_eq!(cx.get(), Some(&ValueA("a")));
422 assert_eq!(cx.get::<ValueB>(), None);
423 true
424 }));
425 }
426}