1use std::cell::RefCell;
2use std::collections::HashMap;
3use std::future::Future;
4use std::pin::Pin;
5use std::task::{Context, Poll};
6
7pub const WITH_CONTEXT_ENABLED: bool = cfg!(feature = "with_context");
8
9thread_local! {
10 static CONTEXT_STACK: RefCell<Vec<HashMap<String, String>>> = const { RefCell::new(Vec::new()) };
11 static ASYNC_STACK_TL: RefCell<Vec<HashMap<String, String>>> = const { RefCell::new(Vec::new()) };
12}
13
14pub fn set_global_context(_: &str, _: &str) {}
15pub fn get_global_context() -> Option<HashMap<String, String>> {
16 None
17}
18
19pub fn get_context_merged() -> HashMap<String, String> {
20 let mut ctx = ASYNC_STACK_TL.with(|s| {
21 let b = s.borrow();
22 b.iter().fold(HashMap::new(), |mut acc, c| {
23 acc.extend(c.clone());
24 acc
25 })
26 });
27 ctx.extend(CONTEXT_STACK.with(|s| {
28 let b = s.borrow();
29 b.iter().fold(HashMap::new(), |mut acc, c| {
30 acc.extend(c.clone());
31 acc
32 })
33 }));
34 ctx
35}
36
37pub fn get_context() -> HashMap<String, String> {
38 get_context_merged()
39}
40pub fn get_async_context() -> HashMap<String, String> {
41 get_context_merged()
42}
43pub fn get_context_all() -> HashMap<String, String> {
44 get_context_merged()
45}
46pub fn get_context_value(key: &str) -> Option<String> {
47 get_context_merged().get(key).cloned()
48}
49pub fn get_context_value_merged(key: &str) -> Option<String> {
50 get_context_value(key)
51}
52
53pub struct ContextFut<F> {
54 fut: F,
55 stack: Vec<HashMap<String, String>>,
56}
57
58impl<F: Future> Future for ContextFut<F> {
59 type Output = F::Output;
60 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
61 let this = unsafe { self.get_unchecked_mut() };
62 let old_stack = ASYNC_STACK_TL.with(|s| {
63 let old = s.borrow().clone();
64 *s.borrow_mut() = this.stack.clone();
65 old
66 });
67 let res = unsafe { Pin::new_unchecked(&mut this.fut) }.poll(cx);
68 ASYNC_STACK_TL.with(|s| *s.borrow_mut() = old_stack);
69 res
70 }
71}
72
73pub async fn with_async_context<F, R>(new_context: HashMap<String, String>, fut: F) -> R
74where
75 F: Future<Output = R>,
76{
77 let mut stack = ASYNC_STACK_TL.with(|s| s.borrow().clone());
78 stack.push(new_context);
79 ContextFut { fut, stack }.await
80}
81
82pub fn scope_current_context<F: Future>(fut: F) -> impl Future<Output = F::Output> {
83 let stack = ASYNC_STACK_TL.with(|s| s.borrow().clone());
84 ContextFut { fut, stack }
85}
86
87pub fn instrument_spawn<F: Future>(fut: F) -> impl Future<Output = F::Output> {
88 scope_current_context(::tracing::Instrument::instrument(
89 fut,
90 ::tracing::Span::current(),
91 ))
92}
93
94#[macro_export]
95macro_rules! log_with_context {
96 ($log_macro:path, $context:expr, $($args:tt)*) => {
97 {
98 let ctx = $context;
99 if !$crate::WITH_CONTEXT_ENABLED {
100 $log_macro!($($args)*);
101 } else {
102 $log_macro!(context = ?ctx, $($args)*);
103 }
104 }
105 };
106}
107
108#[macro_export]
109macro_rules! info { ($($t:tt)*) => { $crate::log_with_context!(::tracing::info, $crate::get_context_merged(), $($t)*) }; }
110#[macro_export]
111macro_rules! warn { ($($t:tt)*) => { $crate::log_with_context!(::tracing::warn, $crate::get_context_merged(), $($t)*) }; }
112#[macro_export]
113macro_rules! error { ($($t:tt)*) => { $crate::log_with_context!(::tracing::error, $crate::get_context_merged(), $($t)*) }; }
114#[macro_export]
115macro_rules! debug { ($($t:tt)*) => { $crate::log_with_context!(::tracing::debug, $crate::get_context_merged(), $($t)*) }; }
116#[macro_export]
117macro_rules! trace { ($($t:tt)*) => { $crate::log_with_context!(::tracing::trace, $crate::get_context_merged(), $($t)*) }; }
118
119pub struct ContextGuard;
120impl Drop for ContextGuard {
121 fn drop(&mut self) {
122 CONTEXT_STACK.with(|s| {
123 s.borrow_mut().pop();
124 });
125 }
126}
127pub fn push_context(c: HashMap<String, String>) -> ContextGuard {
128 CONTEXT_STACK.with(|s| {
129 s.borrow_mut().push(c);
130 });
131 ContextGuard
132}
133pub fn capture_context() -> ContextGuard {
134 push_context(get_context_merged())
135}
136
137pub fn get_inherited_context_string() -> String {
138 let ctx = get_context_merged();
139 ctx.iter()
140 .map(|(k, v)| format!("{}={}", k, v))
141 .collect::<Vec<_>>()
142 .join(",")
143}
144
145pub fn get_inherited_fields_map() -> HashMap<String, String> {
146 get_context_merged()
147}
148
149#[doc(hidden)]
150pub struct AsyncContextGuard;
151impl Drop for AsyncContextGuard {
152 fn drop(&mut self) {}
153}
154#[doc(hidden)]
155pub fn push_async_context(_: HashMap<String, String>) -> AsyncContextGuard {
156 AsyncContextGuard
157}
158#[doc(hidden)]
159pub fn get_current_async_stack() -> Vec<HashMap<String, String>> {
160 ASYNC_STACK_TL.with(|s| s.borrow().clone())
161}
162#[doc(hidden)]
163pub fn get_current_task_context() -> HashMap<String, String> {
164 get_context_merged()
165}
166#[doc(hidden)]
167pub static TASK_CONTEXT_STACK: () = ();
168#[doc(hidden)]
169pub static TASK_CONTEXT_STACK_UNIT: () = ();