sirlog/
macros.rs

1/// logs a single message at the level provided
2#[macro_export]
3macro_rules! log {
4    ($level:expr, $message:expr) => {
5        $crate::Log::new($level, $message).submit()
6    };
7    ($level:expr, $message:expr, $($key:expr => $value:expr),+) => {{
8        let mut log = $crate::Log::new($level, $message);
9        $(
10            log.add($key, $value).unwrap();
11        )+
12        log.submit()
13    }};
14}
15
16#[rustfmt::skip] // This macro confuses rustfmt and it tries to indent it with the inner lines very far to the right.
17macro_rules! make_level_log_macro {
18    // Passing the $ as a token is the workaround to allow parsing the repeated expression https://github.com/rust-lang/rust/issues/35853
19    ($d:tt $name:ident, $level:ident) => {
20        /// logs a message with the appropriate log level
21        // Can't generate a good doc string: https://github.com/rust-lang/rust/issues/37903
22        #[macro_export]
23        macro_rules! $name {
24            ($message:expr) => {
25                $crate::log!($crate::Level::$level, $message)
26            };
27            ($message:expr, $d($d params:tt)*) => {{
28                $crate::log!($crate::Level::$level, $message, $d($d params)*)
29            }};
30        }
31    };
32}
33
34make_level_log_macro!($ error, Error);
35make_level_log_macro!($ warn, Warning);
36make_level_log_macro!($ info, Info);
37make_level_log_macro!($ debug, Debug);
38make_level_log_macro!($ trace, Trace);
39
40#[tokio::test]
41async fn macro_tests() {
42    use crate::{backend::Memory, Level, Manager};
43    use std::time::Duration;
44
45    let test_backend = Memory::new(2);
46    let entries = test_backend.entries.clone();
47    let destination = Manager::default()
48        .with_backend(test_backend)
49        .launch(|task| {
50            tokio::spawn(task);
51        });
52
53    let tests = async move {
54        log!(Level::Info, "A");
55        tokio::time::sleep(Duration::from_millis(1)).await;
56        {
57            let entries = entries.lock().await;
58            assert_eq!(entries[0].level, Level::Info);
59            assert_eq!(entries[0].message, "A");
60        }
61        log!(Level::Info, "B", "a" => 1_u64);
62        tokio::time::sleep(Duration::from_millis(1)).await;
63        {
64            let entries = entries.lock().await;
65            assert_eq!(entries[0].level, Level::Info);
66            assert_eq!(entries[0].payload, serde_json::json!({"a": 1_u64}));
67        }
68
69        macro_rules! test_log_level {
70            ($macroname:ident, $level:expr) => {{
71                $macroname!("A");
72                tokio::time::sleep(Duration::from_millis(1)).await;
73                {
74                    let entries = entries.lock().await;
75                    assert_eq!(entries[0].level, $level);
76                    assert_eq!(entries[0].message, "A");
77                }
78                $macroname!("B", "a" => 1_u64);
79                tokio::time::sleep(Duration::from_millis(1)).await;
80                {
81                    let entries = entries.lock().await;
82                    assert_eq!(entries[0].level, $level);
83                    assert_eq!(entries[0].payload, serde_json::json!({"a": 1_u64}));
84                }
85            }}
86        }
87
88        test_log_level!(error, Level::Error);
89        test_log_level!(warn, Level::Warning);
90        test_log_level!(info, Level::Info);
91        test_log_level!(debug, Level::Debug);
92        test_log_level!(trace, Level::Trace);
93    };
94
95    crate::Configuration::named("macros", destination)
96        .run(tests)
97        .await;
98}