1pub use log as rs_log;
5
6pub use crate::pico_debug as debug;
7pub use crate::pico_error as error;
8pub use crate::pico_info as info;
9pub use crate::pico_warn as warn;
10
11pub trait RequestIdOwner {
12 fn request_id(&self) -> &str;
13}
14
15#[macro_export]
16macro_rules! pico_error {
17 (ctx: $ctx:expr, target: $target:expr, $fmt:expr, $($arg:tt)*) => {
18 $crate::log::rs_log::error!(target: $target, concat!("[{}]: ", $fmt), $crate::log::RequestIdOwner::request_id($ctx), $($arg)*)
19 };
20 (ctx: $ctx:expr, $fmt:expr, $($arg:tt)*) => {
21 $crate::log::rs_log::error!(concat!("[{}]: ", $fmt), $crate::log::RequestIdOwner::request_id($ctx), $($arg)*)
22 };
23 (target: $target:expr, $($arg:tt)+) => {
24 $crate::log::rs_log::error!(target: $target, $($arg)+)
25 };
26 ($($arg:tt)+) => {
27 $crate::log::rs_log::error!($($arg)+)
28 };
29 }
30
31#[macro_export]
32macro_rules! pico_warn {
33 (ctx: $ctx:expr, target: $target:expr, $fmt:expr, $($arg:tt)*) => {
34 $crate::log::rs_log::warn!(target: $target, concat!("[{}]: ", $fmt), $crate::log::RequestIdOwner::request_id($ctx), $($arg)*)
35 };
36 (ctx: $ctx:expr, $fmt:expr, $($arg:tt)*) => {
37 $crate::log::rs_log::warn!(concat!("[{}]: ", $fmt), $crate::log::RequestIdOwner::request_id($ctx), $($arg)*)
38 };
39 (target: $target:expr, $($arg:tt)+) => {
40 $crate::log::rs_log::warn!(target: $target, $($arg)+)
41 };
42 ($($arg:tt)+) => {
43 $crate::log::rs_log::warn!($($arg)+)
44 };
45 }
46
47#[macro_export]
48macro_rules! pico_info {
49 (ctx: $ctx:expr, target: $target:expr, $fmt:expr, $($arg:tt)*) => {
50 $crate::log::rs_log::info!(target: $target, concat!("[{}]: ", $fmt), $crate::log::RequestIdOwner::request_id($ctx), $($arg)*)
51 };
52 (ctx: $ctx:expr, $fmt:expr, $($arg:tt)*) => {
53 $crate::log::rs_log::info!(concat!("[{}]: ", $fmt), $crate::log::RequestIdOwner::request_id($ctx), $($arg)*)
54 };
55 (target: $target:expr, $($arg:tt)+) => {
56 $crate::log::rs_log::info!(target: $target, $($arg)+)
57 };
58 ($($arg:tt)+) => {
59 $crate::log::rs_log::info!($($arg)+)
60 };
61 }
62
63#[macro_export]
64macro_rules! pico_debug {
65 (ctx: $ctx:expr, target: $target:expr, $fmt:expr, $($arg:tt)*) => {
66 $crate::log::rs_log::debug!(target: $target, concat!("[{}]: ", $fmt), $crate::log::RequestIdOwner::request_id($ctx), $($arg)*)
67 };
68 (ctx: $ctx:expr, $fmt:expr, $($arg:tt)*) => {
69 $crate::log::rs_log::debug!(concat!("[{}]: ", $fmt), $crate::log::RequestIdOwner::request_id($ctx), $($arg)*)
70 };
71 (target: $target:expr, $($arg:tt)+) => {
72 $crate::log::rs_log::debug!(target: $target, $($arg)+)
73 };
74 ($($arg:tt)+) => {
75 $crate::log::rs_log::debug!($($arg)+)
76 };
77 }
78
79#[cfg(test)]
80mod test {
81 use crate::log::RequestIdOwner;
82 use log::{Level, LevelFilter, Log, Metadata, Record};
83 use std::sync::Mutex;
84 use std::sync::OnceLock;
85
86 struct BufferedLogger {
87 buff: Mutex<Vec<(Level, String, String)>>,
88 }
89
90 impl Log for BufferedLogger {
91 fn enabled(&self, _metadata: &Metadata) -> bool {
92 true
93 }
94
95 fn log(&self, record: &Record) {
96 self.buff.lock().unwrap().push((
97 record.level(),
98 record.target().to_string(),
99 record.args().to_string(),
100 ))
101 }
102
103 fn flush(&self) {
104 self.buff.lock().unwrap().clear();
105 }
106 }
107
108 struct TestContext {
109 rid: &'static str,
110 }
111
112 impl RequestIdOwner for TestContext {
113 fn request_id(&self) -> &str {
114 self.rid
115 }
116 }
117
118 static LOGGER: OnceLock<BufferedLogger> = OnceLock::new();
119
120 fn logger() -> &'static BufferedLogger {
121 LOGGER.get_or_init(|| BufferedLogger {
122 buff: Mutex::default(),
123 })
124 }
125
126 #[test]
127 fn test_logger() {
128 log::set_logger(logger())
129 .map(|()| log::set_max_level(LevelFilter::Debug))
130 .unwrap();
131
132 test_logger_simple();
133 test_logger_with_custom_target();
134 test_logger_with_request_id();
135 test_logger_with_custom_target_and_request_id();
136 }
137
138 fn test_logger_simple() {
139 logger().flush();
140
141 pico_debug!("simple log record {}", 1);
142 pico_info!("simple log record {}", 2);
143 pico_warn!("simple log record {}", 3);
144 pico_error!("simple log record {}", 4);
145
146 assert_eq!(
147 &[
148 (
149 Level::Debug,
150 "picodata_plugin::log::test".to_string(),
151 "simple log record 1".to_string()
152 ),
153 (
154 Level::Info,
155 "picodata_plugin::log::test".to_string(),
156 "simple log record 2".to_string()
157 ),
158 (
159 Level::Warn,
160 "picodata_plugin::log::test".to_string(),
161 "simple log record 3".to_string()
162 ),
163 (
164 Level::Error,
165 "picodata_plugin::log::test".to_string(),
166 "simple log record 4".to_string()
167 ),
168 ],
169 logger().buff.lock().unwrap().as_slice()
170 );
171 }
172
173 fn test_logger_with_custom_target() {
174 logger().flush();
175
176 pico_debug!(target: "test", "log record");
177 pico_info!(target: "test","log record");
178 pico_warn!(target: "test","log record");
179 pico_error!(target: "test", "log record");
180
181 assert_eq!(
182 &[
183 (Level::Debug, "test".to_string(), "log record".to_string()),
184 (Level::Info, "test".to_string(), "log record".to_string()),
185 (Level::Warn, "test".to_string(), "log record".to_string()),
186 (Level::Error, "test".to_string(), "log record".to_string()),
187 ],
188 logger().buff.lock().unwrap().as_slice()
189 );
190 }
191
192 fn test_logger_with_request_id() {
193 logger().flush();
194
195 let ctx = TestContext { rid: "12345" };
196
197 pico_debug!(ctx: &ctx, "{}", "log record");
198 pico_info!(ctx: &ctx, "{}", "log record");
199 pico_warn!(ctx: &ctx, "{}", "log record");
200 pico_error!(ctx: &ctx, "{}", "log record");
201
202 assert_eq!(
203 &[
204 (
205 Level::Debug,
206 "picodata_plugin::log::test".to_string(),
207 "[12345]: log record".to_string()
208 ),
209 (
210 Level::Info,
211 "picodata_plugin::log::test".to_string(),
212 "[12345]: log record".to_string()
213 ),
214 (
215 Level::Warn,
216 "picodata_plugin::log::test".to_string(),
217 "[12345]: log record".to_string()
218 ),
219 (
220 Level::Error,
221 "picodata_plugin::log::test".to_string(),
222 "[12345]: log record".to_string()
223 ),
224 ],
225 logger().buff.lock().unwrap().as_slice()
226 );
227 }
228
229 fn test_logger_with_custom_target_and_request_id() {
230 logger().flush();
231
232 let ctx = TestContext { rid: "12345" };
233
234 pico_debug!(ctx: &ctx, target: "test", "{}", "log record");
235 pico_info!(ctx: &ctx, target: "test", "{}", "log record");
236 pico_warn!(ctx: &ctx, target: "test", "{}", "log record");
237 pico_error!(ctx: &ctx, target: "test", "{}", "log record");
238
239 assert_eq!(
240 &[
241 (
242 Level::Debug,
243 "test".to_string(),
244 "[12345]: log record".to_string()
245 ),
246 (
247 Level::Info,
248 "test".to_string(),
249 "[12345]: log record".to_string()
250 ),
251 (
252 Level::Warn,
253 "test".to_string(),
254 "[12345]: log record".to_string()
255 ),
256 (
257 Level::Error,
258 "test".to_string(),
259 "[12345]: log record".to_string()
260 ),
261 ],
262 logger().buff.lock().unwrap().as_slice()
263 );
264 }
265}