1use std::cell::RefCell;
2use std::collections::HashMap;
3use std::sync::{Arc, Mutex};
4
5static GLOBAL_CONTEXT: std::sync::LazyLock<Arc<Mutex<HashMap<String, String>>>> =
7 std::sync::LazyLock::new(|| Arc::new(Mutex::new(HashMap::new())));
8
9pub fn set_global_context(key: &str, value: &str) {
11 if let Ok(mut global) = GLOBAL_CONTEXT.lock() {
12 global.insert(key.to_string(), value.to_string());
13 }
14}
15
16pub fn get_global_context() -> Option<HashMap<String, String>> {
18 if let Ok(global) = GLOBAL_CONTEXT.lock() {
19 if !global.is_empty() {
20 return Some(global.clone());
21 }
22 }
23 None
24}
25
26thread_local! {
28 static CONTEXT_STACK: RefCell<Vec<HashMap<String, String>>> = RefCell::new(Vec::new());
29 static ASYNC_CONTEXT_STACK: RefCell<Vec<HashMap<String, String>>> = RefCell::new(Vec::new());
30}
31
32#[doc(hidden)]
34pub struct ContextGuard;
35
36impl Drop for ContextGuard {
37 fn drop(&mut self) {
38 CONTEXT_STACK.with(|stack| {
39 stack.borrow_mut().pop();
40 });
41 }
42}
43
44pub fn get_context_value(key: &str) -> Option<String> {
46 if let Ok(stack) = ASYNC_CONTEXT_STACK.try_with(|stack| stack.borrow().clone()) {
48 for context_map in stack.iter().rev() {
49 if let Some(value) = context_map.get(key) {
50 return Some(value.clone());
51 }
52 }
53 }
54
55 let result = CONTEXT_STACK.with(|stack| {
57 let stack = stack.borrow();
58 for context_map in stack.iter().rev() {
59 if let Some(value) = context_map.get(key) {
60 return Some(value.clone());
61 }
62 }
63 None
64 });
65
66 if result.is_some() {
67 return result;
68 }
69
70 if let Ok(global) = GLOBAL_CONTEXT.lock() {
72 if let Some(value) = global.get(key) {
73 return Some(value.clone());
74 }
75 }
76
77 None
78}
79
80#[doc(hidden)]
82pub fn get_context() -> HashMap<String, String> {
83 CONTEXT_STACK.with(|stack| {
84 stack
85 .borrow()
86 .iter()
87 .fold(HashMap::new(), |mut acc, context| {
88 acc.extend(context.clone());
89 acc
90 })
91 })
92}
93
94#[doc(hidden)]
95pub fn get_async_context() -> HashMap<String, String> {
96 ASYNC_CONTEXT_STACK
97 .try_with(|stack| {
98 stack
99 .borrow()
100 .iter()
101 .fold(HashMap::new(), |mut acc, context| {
102 acc.extend(context.clone());
103 acc
104 })
105 })
106 .unwrap_or_default()
107}
108
109#[doc(hidden)]
110pub fn get_current_async_stack() -> Vec<HashMap<String, String>> {
111 ASYNC_CONTEXT_STACK
112 .try_with(|stack| stack.borrow().clone())
113 .unwrap_or_else(|_| vec![HashMap::new()])
114}
115
116#[doc(hidden)]
118pub fn push_context(context: HashMap<String, String>) -> ContextGuard {
119 CONTEXT_STACK.with(|stack| {
120 stack.borrow_mut().push(context);
121 });
122 ContextGuard
123}
124
125#[doc(hidden)]
127pub fn push_async_context(context: HashMap<String, String>) -> AsyncContextGuard {
128 ASYNC_CONTEXT_STACK.with(|stack| {
129 stack.borrow_mut().push(context);
130 });
131 AsyncContextGuard
132}
133
134pub struct AsyncContextGuard;
136
137impl Drop for AsyncContextGuard {
138 fn drop(&mut self) {
139 ASYNC_CONTEXT_STACK.with(|stack| {
140 stack.borrow_mut().pop();
141 });
142 }
143}
144
145#[macro_export]
148macro_rules! add_context_fields {
149 ($log_macro:path, $ctx:expr, $($args:tt)*) => {
150 let mut field_tokens = Vec::new();
153
154 for (key, value) in $ctx.iter() {
156 let field_token = if key.contains('.') {
158 format!("\"{key}\" = %{value}", key = key, value = value)
160 } else {
161 format!("{key} = %{value}", key = key, value = value)
163 };
164 field_tokens.push(field_token);
165 }
166
167 };
171}
172
173#[macro_export]
174macro_rules! log_with_context {
175 ($log_macro:path, $context:expr, $($args:tt)*) => {
176 {
177 let ctx = $context;
178 if ctx.is_empty() {
179 $log_macro!($($args)*);
180 } else {
181 let span = ::tracing::info_span!(
190 "context",
191 );
194
195 for (key, value) in ctx.iter() {
197 span.record(key.as_str(), &::tracing::field::display(value));
198 }
199
200 let _enter = span.enter();
202 $log_macro!($($args)*);
203
204 drop(_enter);
207 drop(span);
208
209 let mut context_fields = Vec::new();
211 for (key, value) in ctx.iter() {
212 context_fields.push(format!("{}={}", key, value));
213 }
214 if !context_fields.is_empty() {
215 let context_data = context_fields.join(" ");
216 $log_macro!(context = %context_data, $($args)*);
219 } else {
220 $log_macro!($($args)*);
221 }
222 }
223 }
224 };
225}
226
227#[macro_export]
230macro_rules! info {
231 ($($t:tt)*) => {
232 $crate::log_with_context!(::tracing::info, $crate::get_context(), $($t)*);
233 };
234}
235
236#[macro_export]
237macro_rules! warn {
238 ($($t:tt)*) => {
239 $crate::log_with_context!(::tracing::warn, $crate::get_context(), $($t)*);
240 };
241}
242
243#[macro_export]
244macro_rules! error {
245 ($($t:tt)*) => {
246 $crate::log_with_context!(::tracing::error, $crate::get_context(), $($t)*);
247 };
248}
249
250#[macro_export]
251macro_rules! debug {
252 ($($t:tt)*) => {
253 $crate::log_with_context!(::tracing::debug, $crate::get_context(), $($t)*);
254 };
255}
256
257#[macro_export]
258macro_rules! trace {
259 ($($t:tt)*) => {
260 $crate::log_with_context!(::tracing::trace, $crate::get_context(), $($t)*);
261 };
262}
263
264pub fn auto_capture_context() -> ContextGuard {
267 let current_context = get_context();
268
269 let _async_guard = push_async_context(current_context.clone());
271 let _sync_guard = push_context(current_context);
272
273 ContextGuard
275}
276
277pub fn capture_context() -> ContextGuard {
280 let current_context = get_context();
281
282 for (key, value) in ¤t_context {
284 set_global_context(key, value);
285 }
286
287 let _async_guard = push_async_context(current_context.clone());
289 let _sync_guard = push_context(current_context);
290
291 ContextGuard
293}
294
295pub fn with_context_capture1<F, A, R>(f: F) -> impl FnOnce(A) -> R
299where
300 F: FnOnce(A) -> R,
301{
302 let captured_context = get_context();
303
304 move |a| {
305 let _guard = push_async_context(captured_context.clone());
307
308 let _sync_guard = push_context(captured_context);
310
311 f(a)
312 }
313}
314
315pub fn with_context_capture2<F, A, B, R>(f: F) -> impl FnOnce(A, B) -> R
317where
318 F: FnOnce(A, B) -> R,
319{
320 let captured_context = get_context();
321
322 move |a, b| {
323 let _guard = push_context(captured_context);
324 f(a, b)
325 }
326}
327
328pub fn with_context_capture3<F, A, B, C, R>(f: F) -> impl FnOnce(A, B, C) -> R
330where
331 F: FnOnce(A, B, C) -> R,
332{
333 let captured_context = get_context();
334
335 move |a, b, c| {
336 let _guard = push_context(captured_context);
337 f(a, b, c)
338 }
339}
340
341pub fn get_inherited_context_string() -> String {
345 let mut context_parts = Vec::new();
346
347 let current_span = tracing::Span::current();
349 if !current_span.is_none() {
350 }
354
355 if let Ok(stack) = ASYNC_CONTEXT_STACK.try_with(|stack| stack.borrow().clone()) {
357 for context_map in stack.iter().rev() {
359 for (key, value) in context_map {
360 if key != "function" && !context_parts.iter().any(|p: &String| p.starts_with(&format!("{}=", key))) {
362 context_parts.push(format!("{}={}", key, value));
363 }
364 }
365 }
366 }
367
368 CONTEXT_STACK.with(|stack| {
370 let stack = stack.borrow();
371 for context_map in stack.iter().rev() {
372 for (key, value) in context_map {
373 if key != "function" && !context_parts.iter().any(|p: &String| p.starts_with(&format!("{}=", key))) {
375 context_parts.push(format!("{}={}", key, value));
376 }
377 }
378 }
379 });
380
381 if context_parts.is_empty() {
383 if let Some(global_context) = get_global_context() {
384 for (key, value) in global_context {
385 if key != "function" {
386 context_parts.push(format!("{}={}", key, value));
387 }
388 }
389 }
390 }
391
392 if context_parts.is_empty() {
393 "<no_context>".to_string()
394 } else {
395 context_parts.join(",")
396 }
397}
398
399pub fn get_inherited_fields_map() -> std::collections::HashMap<String, String> {
402 let mut context_map = std::collections::HashMap::new();
403
404 if let Ok(stack) = ASYNC_CONTEXT_STACK.try_with(|stack| stack.borrow().clone()) {
406 for stack_context in stack.iter().rev() {
407 for (key, value) in stack_context {
408 if key != "function" {
410 context_map.insert(key.clone(), value.clone());
411 }
412 }
413 if !context_map.is_empty() {
414 return context_map; }
416 }
417 }
418
419 if context_map.is_empty() {
421 CONTEXT_STACK.with(|stack| {
422 let stack = stack.borrow();
423 for stack_context in stack.iter().rev() {
424 for (key, value) in stack_context {
425 if key != "function" {
427 context_map.insert(key.clone(), value.clone());
428 }
429 }
430 if !context_map.is_empty() {
431 return; }
433 }
434 });
435 }
436
437 context_map
438}