1use once_cell::sync::Lazy;
30use std::cell::RefCell;
31use std::collections::HashMap;
32use std::sync::{Arc, Mutex};
33
34pub const WITH_CONTEXT_ENABLED: bool = cfg!(feature = "with_context");
37
38static GLOBAL_CONTEXT: Lazy<Arc<Mutex<HashMap<String, String>>>> =
40 Lazy::new(|| Arc::new(Mutex::new(HashMap::new())));
41
42pub fn set_global_context(key: &str, value: &str) {
44 if let Ok(mut global) = GLOBAL_CONTEXT.lock() {
45 global.insert(key.to_string(), value.to_string());
46 }
47}
48
49pub fn get_global_context() -> Option<HashMap<String, String>> {
51 if let Ok(global) = GLOBAL_CONTEXT.lock() {
52 if !global.is_empty() {
53 return Some(global.clone());
54 }
55 }
56 None
57}
58
59thread_local! {
61 static CONTEXT_STACK: RefCell<Vec<HashMap<String, String>>> = const { RefCell::new(Vec::new()) };
62 static ASYNC_CONTEXT_STACK: RefCell<Vec<HashMap<String, String>>> = const { RefCell::new(Vec::new()) };
63}
64
65#[doc(hidden)]
67pub struct ContextGuard;
68
69impl Drop for ContextGuard {
70 fn drop(&mut self) {
71 CONTEXT_STACK.with(|stack| {
72 stack.borrow_mut().pop();
73 });
74 }
75}
76
77pub fn get_context_value(key: &str) -> Option<String> {
79 if let Ok(stack) = ASYNC_CONTEXT_STACK.try_with(|stack| stack.borrow().clone()) {
81 for context_map in stack.iter().rev() {
82 if let Some(value) = context_map.get(key) {
83 return Some(value.clone());
84 }
85 }
86 }
87
88 let result = CONTEXT_STACK.with(|stack| {
90 let stack = stack.borrow();
91 for context_map in stack.iter().rev() {
92 if let Some(value) = context_map.get(key) {
93 return Some(value.clone());
94 }
95 }
96 None
97 });
98
99 if result.is_some() {
100 return result;
101 }
102
103 if let Ok(global) = GLOBAL_CONTEXT.lock() {
105 if let Some(value) = global.get(key) {
106 return Some(value.clone());
107 }
108 }
109
110 None
111}
112
113#[doc(hidden)]
115pub fn get_context() -> HashMap<String, String> {
116 CONTEXT_STACK.with(|stack| {
117 stack
118 .borrow()
119 .iter()
120 .fold(HashMap::new(), |mut acc, context| {
121 acc.extend(context.clone());
122 acc
123 })
124 })
125}
126
127#[doc(hidden)]
128pub fn get_async_context() -> HashMap<String, String> {
129 ASYNC_CONTEXT_STACK
130 .try_with(|stack| {
131 stack
132 .borrow()
133 .iter()
134 .fold(HashMap::new(), |mut acc, context| {
135 acc.extend(context.clone());
136 acc
137 })
138 })
139 .unwrap_or_default()
140}
141
142#[doc(hidden)]
143pub fn get_current_async_stack() -> Vec<HashMap<String, String>> {
144 ASYNC_CONTEXT_STACK
145 .try_with(|stack| stack.borrow().clone())
146 .unwrap_or_else(|_| vec![HashMap::new()])
147}
148
149#[doc(hidden)]
151pub fn push_context(context: HashMap<String, String>) -> ContextGuard {
152 CONTEXT_STACK.with(|stack| {
153 stack.borrow_mut().push(context);
154 });
155 ContextGuard
156}
157
158#[doc(hidden)]
160pub fn push_async_context(context: HashMap<String, String>) -> AsyncContextGuard {
161 ASYNC_CONTEXT_STACK.with(|stack| {
162 stack.borrow_mut().push(context);
163 });
164 AsyncContextGuard
165}
166
167pub struct AsyncContextGuard;
169
170impl Drop for AsyncContextGuard {
171 fn drop(&mut self) {
172 ASYNC_CONTEXT_STACK.with(|stack| {
173 stack.borrow_mut().pop();
174 });
175 }
176}
177
178#[macro_export]
179macro_rules! log_with_context {
180 ($log_macro:path, $context:expr, $($args:tt)*) => {
181 {
182 let ctx = $context;
183 if !$crate::WITH_CONTEXT_ENABLED {
185 $log_macro!($($args)*);
186 } else {
187 $log_macro!(context = ?ctx, $($args)*);
190 }
191 }
192 };
193}
194
195#[macro_export]
198macro_rules! info {
199 ($($t:tt)*) => {
200 $crate::log_with_context!(::tracing::info, $crate::get_context(), $($t)*)
201 };
202}
203
204#[macro_export]
205macro_rules! warn {
206 ($($t:tt)*) => {
207 $crate::log_with_context!(::tracing::warn, $crate::get_context(), $($t)*)
208 };
209}
210
211#[macro_export]
212macro_rules! error {
213 ($($t:tt)*) => {
214 $crate::log_with_context!(::tracing::error, $crate::get_context(), $($t)*)
215 };
216}
217
218#[macro_export]
219macro_rules! debug {
220 ($($t:tt)*) => {
221 $crate::log_with_context!(::tracing::debug, $crate::get_context(), $($t)*)
222 };
223}
224
225#[macro_export]
226macro_rules! trace {
227 ($($t:tt)*) => {
228 $crate::log_with_context!(::tracing::trace, $crate::get_context(), $($t)*)
229 };
230}
231
232pub fn auto_capture_context() -> ContextGuard {
235 let current_context = get_context();
236
237 let _async_guard = push_async_context(current_context.clone());
239 let _sync_guard = push_context(current_context);
240
241 ContextGuard
243}
244
245pub fn capture_context() -> ContextGuard {
248 let mut current_context = get_async_context();
250 let sync_ctx = get_context();
251 current_context.extend(sync_ctx);
252
253 for (key, value) in ¤t_context {
255 set_global_context(key, value);
256 }
257
258 let _async_guard = push_async_context(current_context.clone());
260 let _sync_guard = push_context(current_context);
261
262 ContextGuard
264}
265
266pub fn get_inherited_context_string() -> String {
270 let mut context_parts = Vec::new();
271
272 let current_span = tracing::Span::current();
274 if !current_span.is_none() {
275 }
279
280 if let Ok(stack) = ASYNC_CONTEXT_STACK.try_with(|stack| stack.borrow().clone()) {
282 fill_context_parts(&mut context_parts, &stack);
283 }
284
285 CONTEXT_STACK.with(|stack| {
287 let stack = stack.borrow();
288 fill_context_parts(&mut context_parts, &stack);
289 });
290
291 if context_parts.is_empty() {
293 if let Some(global_context) = get_global_context() {
294 for (key, value) in global_context {
295 if key != "function" {
296 context_parts.push(format!("{key}={value}"));
297 }
298 }
299 }
300 }
301
302 if context_parts.is_empty() {
303 "".to_string()
304 } else {
305 context_parts.join(",")
306 }
307}
308
309pub fn get_inherited_fields_map() -> std::collections::HashMap<String, String> {
312 let mut context_map = std::collections::HashMap::new();
313
314 if let Ok(stack) = ASYNC_CONTEXT_STACK.try_with(|stack| stack.borrow().clone()) {
316 for stack_context in stack.iter().rev() {
317 for (key, value) in stack_context {
318 if key != "function" {
320 context_map.insert(key.clone(), value.clone());
321 }
322 }
323 if !context_map.is_empty() {
324 return context_map; }
326 }
327 }
328
329 if context_map.is_empty() {
331 CONTEXT_STACK.with(|stack| {
332 let stack = stack.borrow();
333 for stack_context in stack.iter().rev() {
334 for (key, value) in stack_context {
335 if key != "function" {
337 context_map.insert(key.clone(), value.clone());
338 }
339 }
340 if !context_map.is_empty() {
341 return; }
343 }
344 });
345 }
346
347 context_map
348}
349
350fn fill_context_parts(context_parts: &mut Vec<String>, stack: &[HashMap<String, String>]) {
351 for context_map in stack.iter().rev() {
352 for (key, value) in context_map {
353 if key != "function"
355 && !context_parts
356 .iter()
357 .any(|p: &String| p.starts_with(&format!("{key}=")))
358 {
359 context_parts.push(format!("{key}={value}"));
360 }
361 }
362 }
363}