1use std::marker::PhantomData;
4
5use crate::{
6 LogContext,
7 stack::{CONTEXT_STACK, ContextStack},
8};
9
10#[non_exhaustive]
32#[derive(Debug)]
33pub struct LogContextGuard<'a> {
34 _marker: PhantomData<&'a *mut ()>,
36}
37
38impl LogContextGuard<'_> {
39 pub(crate) fn enter(context: LogContext) -> Self {
40 CONTEXT_STACK.with(|stack| stack.push(context.0));
41 Self {
42 _marker: PhantomData,
43 }
44 }
45
46 pub(crate) fn exit(self) -> LogContext {
47 std::mem::forget(self);
50
51 let properties = CONTEXT_STACK
52 .with(ContextStack::pop)
53 .expect("There is a bug in log context guard, context should be exists");
54 LogContext(properties)
55 }
56}
57
58impl Drop for LogContextGuard<'_> {
59 fn drop(&mut self) {
60 CONTEXT_STACK.with(ContextStack::pop);
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use pretty_assertions::assert_eq;
67
68 use super::*;
69 use crate::stack::CONTEXT_STACK;
70
71 #[test]
72 fn test_log_context_guard_enter() {
73 let context = LogContext::new().record("simple", 42);
74 assert_eq!(CONTEXT_STACK.with(ContextStack::is_empty), true);
76
77 let guard = context.enter();
78 assert_eq!(CONTEXT_STACK.with(|stack| stack.top().unwrap().len()), 1);
80
81 drop(guard);
83 assert_eq!(CONTEXT_STACK.with(ContextStack::len), 0);
84 }
85
86 #[test]
87 fn test_log_context_nested_guards() {
88 let outer_context = LogContext::new().record("simple_record", "outer_value");
89 assert_eq!(CONTEXT_STACK.with(ContextStack::len), 0);
90
91 let outer_guard = outer_context.enter();
92 assert_eq!(CONTEXT_STACK.with(|stack| stack.top().unwrap().len()), 1);
93
94 CONTEXT_STACK.with(|stack| {
95 let property = &stack.top().unwrap()[0];
96 assert_eq!(property.0, "simple_record");
97 assert_eq!(property.1.to_string(), "outer_value");
98 });
99
100 let inner_context = LogContext::new().record("simple_record", "inner_value");
101 {
102 let inner_guard = inner_context.enter();
103 assert_eq!(CONTEXT_STACK.with(ContextStack::len), 2);
105 CONTEXT_STACK.with(|stack| {
106 let property = &stack.top().unwrap()[0];
107 assert_eq!(property.0, "simple_record");
108 assert_eq!(property.1.to_string(), "inner_value");
109 });
110
111 drop(inner_guard);
112 }
113 assert_eq!(CONTEXT_STACK.with(|stack| stack.top().unwrap().len()), 1);
115 CONTEXT_STACK.with(|stack| {
116 let property = &stack.top().unwrap()[0];
117 assert_eq!(property.0, "simple_record");
118 assert_eq!(property.1.to_string(), "outer_value");
119 });
120
121 drop(outer_guard);
122 assert_eq!(CONTEXT_STACK.with(ContextStack::is_empty), true);
123 }
124
125 #[test]
126 fn test_log_context_multithread() {
127 let local_context = LogContext::new().record("simple_record", "main");
128 let local_guard = local_context.enter();
129
130 let first_thread_handle = std::thread::spawn(|| {
131 let inner_context = LogContext::new().record("simple_record", "first_thread");
132 let inner_guard = inner_context.enter();
133
134 assert_eq!(CONTEXT_STACK.with(ContextStack::len), 1);
136 CONTEXT_STACK.with(|stack| {
137 let property = &stack.top().unwrap()[0];
138 assert_eq!(property.0, "simple_record");
139 assert_eq!(property.1.to_string(), "first_thread");
140 });
141
142 drop(inner_guard);
143 });
144 let second_thread_handle = std::thread::spawn(|| {
145 let inner_context = LogContext::new().record("simple_record", "second_thread");
146 let inner_guard = inner_context.enter();
147 assert_eq!(CONTEXT_STACK.with(ContextStack::len), 1);
149 CONTEXT_STACK.with(|stack| {
150 let property = &stack.top().unwrap()[0];
151 assert_eq!(property.0, "simple_record");
152 assert_eq!(property.1.to_string(), "second_thread");
153 });
154
155 drop(inner_guard);
156 });
157
158 first_thread_handle.join().unwrap();
159 second_thread_handle.join().unwrap();
160
161 CONTEXT_STACK.with(|stack| {
162 let property = &stack.top().unwrap()[0];
163 assert_eq!(property.0, "simple_record");
164 assert_eq!(property.1.to_string(), "main");
165 });
166 drop(local_guard);
167 }
168}