Skip to main content

picodata_plugin/log/
mod.rs

1//! Log module provides special log marcos for end-to-end logging and ability to change
2//! default tarantool-logger templates (using `tarolog` lib).
3
4pub 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}