1use std::cell::RefCell;
30use std::collections::HashMap;
31use std::sync::{Arc, Mutex};
32
33pub const WITH_CONTEXT_ENABLED: bool = cfg!(feature = "with_context");
36
37static GLOBAL_CONTEXT: std::sync::LazyLock<Arc<Mutex<HashMap<String, String>>>> =
39 std::sync::LazyLock::new(|| Arc::new(Mutex::new(HashMap::new())));
40
41pub fn set_global_context(key: &str, value: &str) {
43 if let Ok(mut global) = GLOBAL_CONTEXT.lock() {
44 global.insert(key.to_string(), value.to_string());
45 }
46}
47
48pub fn get_global_context() -> Option<HashMap<String, String>> {
50 if let Ok(global) = GLOBAL_CONTEXT.lock() {
51 if !global.is_empty() {
52 return Some(global.clone());
53 }
54 }
55 None
56}
57
58thread_local! {
60 static CONTEXT_STACK: RefCell<Vec<HashMap<String, String>>> = RefCell::new(Vec::new());
61 static ASYNC_CONTEXT_STACK: RefCell<Vec<HashMap<String, String>>> = RefCell::new(Vec::new());
62}
63
64#[doc(hidden)]
66pub struct ContextGuard;
67
68impl Drop for ContextGuard {
69 fn drop(&mut self) {
70 CONTEXT_STACK.with(|stack| {
71 stack.borrow_mut().pop();
72 });
73 }
74}
75
76pub fn get_context_value(key: &str) -> Option<String> {
78 if let Ok(stack) = ASYNC_CONTEXT_STACK.try_with(|stack| stack.borrow().clone()) {
80 for context_map in stack.iter().rev() {
81 if let Some(value) = context_map.get(key) {
82 return Some(value.clone());
83 }
84 }
85 }
86
87 let result = CONTEXT_STACK.with(|stack| {
89 let stack = stack.borrow();
90 for context_map in stack.iter().rev() {
91 if let Some(value) = context_map.get(key) {
92 return Some(value.clone());
93 }
94 }
95 None
96 });
97
98 if result.is_some() {
99 return result;
100 }
101
102 if let Ok(global) = GLOBAL_CONTEXT.lock() {
104 if let Some(value) = global.get(key) {
105 return Some(value.clone());
106 }
107 }
108
109 None
110}
111
112#[doc(hidden)]
114pub fn get_context() -> HashMap<String, String> {
115 CONTEXT_STACK.with(|stack| {
116 stack
117 .borrow()
118 .iter()
119 .fold(HashMap::new(), |mut acc, context| {
120 acc.extend(context.clone());
121 acc
122 })
123 })
124}
125
126#[doc(hidden)]
127pub fn get_async_context() -> HashMap<String, String> {
128 ASYNC_CONTEXT_STACK
129 .try_with(|stack| {
130 stack
131 .borrow()
132 .iter()
133 .fold(HashMap::new(), |mut acc, context| {
134 acc.extend(context.clone());
135 acc
136 })
137 })
138 .unwrap_or_default()
139}
140
141#[doc(hidden)]
142pub fn get_current_async_stack() -> Vec<HashMap<String, String>> {
143 ASYNC_CONTEXT_STACK
144 .try_with(|stack| stack.borrow().clone())
145 .unwrap_or_else(|_| vec![HashMap::new()])
146}
147
148#[doc(hidden)]
150pub fn push_context(context: HashMap<String, String>) -> ContextGuard {
151 CONTEXT_STACK.with(|stack| {
152 stack.borrow_mut().push(context);
153 });
154 ContextGuard
155}
156
157#[doc(hidden)]
159pub fn push_async_context(context: HashMap<String, String>) -> AsyncContextGuard {
160 ASYNC_CONTEXT_STACK.with(|stack| {
161 stack.borrow_mut().push(context);
162 });
163 AsyncContextGuard
164}
165
166pub struct AsyncContextGuard;
168
169impl Drop for AsyncContextGuard {
170 fn drop(&mut self) {
171 ASYNC_CONTEXT_STACK.with(|stack| {
172 stack.borrow_mut().pop();
173 });
174 }
175}
176
177#[macro_export]
180macro_rules! add_context_fields {
181 ($log_macro:path, $ctx:expr, $($args:tt)*) => {
182 let mut field_tokens = Vec::new();
185
186 for (key, value) in $ctx.iter() {
188 let field_token = if key.contains('.') {
190 format!("\"{key}\" = %{value}", key = key, value = value)
192 } else {
193 format!("{key} = %{value}", key = key, value = value)
195 };
196 field_tokens.push(field_token);
197 }
198
199 };
203}
204
205#[macro_export]
206macro_rules! log_with_context {
207 ($log_macro:path, $context:expr, $($args:tt)*) => {
208 {
209 let ctx = $context;
210 if !$crate::WITH_CONTEXT_ENABLED {
212 $log_macro!($($args)*);
213 } else {
214 $log_macro!(context = ?ctx, $($args)*);
217 }
218 }
219 };
220}
221
222#[macro_export]
225macro_rules! info {
226 ($($t:tt)*) => {
227 $crate::log_with_context!(::tracing::info, $crate::get_context(), $($t)*)
228 };
229}
230
231#[macro_export]
232macro_rules! warn {
233 ($($t:tt)*) => {
234 $crate::log_with_context!(::tracing::warn, $crate::get_context(), $($t)*)
235 };
236}
237
238#[macro_export]
239macro_rules! error {
240 ($($t:tt)*) => {
241 $crate::log_with_context!(::tracing::error, $crate::get_context(), $($t)*)
242 };
243}
244
245#[macro_export]
246macro_rules! debug {
247 ($($t:tt)*) => {
248 $crate::log_with_context!(::tracing::debug, $crate::get_context(), $($t)*)
249 };
250}
251
252#[macro_export]
253macro_rules! trace {
254 ($($t:tt)*) => {
255 $crate::log_with_context!(::tracing::trace, $crate::get_context(), $($t)*)
256 };
257}
258
259pub fn auto_capture_context() -> ContextGuard {
262 let current_context = get_context();
263
264 let _async_guard = push_async_context(current_context.clone());
266 let _sync_guard = push_context(current_context);
267
268 ContextGuard
270}
271
272pub fn capture_context() -> ContextGuard {
275 let mut current_context = get_async_context();
277 let sync_ctx = get_context();
278 current_context.extend(sync_ctx);
279
280 for (key, value) in ¤t_context {
282 set_global_context(key, value);
283 }
284
285 let _async_guard = push_async_context(current_context.clone());
287 let _sync_guard = push_context(current_context);
288
289 ContextGuard
291}
292
293pub fn get_inherited_context_string() -> String {
297 let mut context_parts = Vec::new();
298
299 let current_span = tracing::Span::current();
301 if !current_span.is_none() {
302 }
306
307 if let Ok(stack) = ASYNC_CONTEXT_STACK.try_with(|stack| stack.borrow().clone()) {
309 for context_map in stack.iter().rev() {
311 for (key, value) in context_map {
312 if key != "function"
314 && !context_parts
315 .iter()
316 .any(|p: &String| p.starts_with(&format!("{key}=")))
317 {
318 context_parts.push(format!("{key}={value}"));
319 }
320 }
321 }
322 }
323
324 CONTEXT_STACK.with(|stack| {
326 let stack = stack.borrow();
327 for context_map in stack.iter().rev() {
328 for (key, value) in context_map {
329 if key != "function"
331 && !context_parts
332 .iter()
333 .any(|p: &String| p.starts_with(&format!("{key}=")))
334 {
335 context_parts.push(format!("{key}={value}"));
336 }
337 }
338 }
339 });
340
341 if context_parts.is_empty() {
343 if let Some(global_context) = get_global_context() {
344 for (key, value) in global_context {
345 if key != "function" {
346 context_parts.push(format!("{key}={value}"));
347 }
348 }
349 }
350 }
351
352 if context_parts.is_empty() {
353 "".to_string()
354 } else {
355 context_parts.join(",")
356 }
357}
358
359pub fn get_inherited_fields_map() -> std::collections::HashMap<String, String> {
362 let mut context_map = std::collections::HashMap::new();
363
364 if let Ok(stack) = ASYNC_CONTEXT_STACK.try_with(|stack| stack.borrow().clone()) {
366 for stack_context in stack.iter().rev() {
367 for (key, value) in stack_context {
368 if key != "function" {
370 context_map.insert(key.clone(), value.clone());
371 }
372 }
373 if !context_map.is_empty() {
374 return context_map; }
376 }
377 }
378
379 if context_map.is_empty() {
381 CONTEXT_STACK.with(|stack| {
382 let stack = stack.borrow();
383 for stack_context in stack.iter().rev() {
384 for (key, value) in stack_context {
385 if key != "function" {
387 context_map.insert(key.clone(), value.clone());
388 }
389 }
390 if !context_map.is_empty() {
391 return; }
393 }
394 });
395 }
396
397 context_map
398}