Browse Source

Add log_with macros for scope-style logging.

The thread local logger now supports logging using a specific logger in a scope.
Jing Yang 4 năm trước cách đây
mục cha
commit
f23cf15b02
3 tập tin đã thay đổi với 148 bổ sung4 xóa
  1. 1 0
      test_utils/src/lib.rs
  2. 143 0
      test_utils/src/log_with.rs
  3. 4 4
      test_utils/src/thread_local_logger.rs

+ 1 - 0
test_utils/src/lib.rs

@@ -1,5 +1,6 @@
 /// Test utilities.
 /// See [`thread_local_logger`] for more details.
+mod log_with;
 mod logging;
 pub mod thread_local_logger;
 pub use logging::{init_log, LOG_DIR};

+ 143 - 0
test_utils/src/log_with.rs

@@ -0,0 +1,143 @@
+#[macro_export]
+macro_rules! log_with {
+    ($local_logger:expr, $log:expr) => {{
+        let prev = $crate::thread_local_logger::get();
+        $crate::thread_local_logger::LocalLogger::attach($local_logger.clone());
+        let val = $log;
+        $crate::thread_local_logger::set(prev);
+        val
+    }};
+    ($local_logger:expr, $($arg:tt)+) => {{
+        $crate::log_with!($local_logger, log::log!($($arg)+));
+    }};
+}
+
+#[macro_export]
+macro_rules! log_trace_with {
+    ($local_logger:expr, $($arg:tt)+) => {{
+        $crate::log_with!($local_logger, log::trace!($($arg)+));
+    }};
+}
+
+#[macro_export]
+macro_rules! log_debug_with {
+    ($local_logger:expr, $($arg:tt)+) => {{
+        $crate::log_with!($local_logger, log::debug!($($arg)+));
+    }};
+}
+
+#[macro_export]
+macro_rules! log_info_with {
+    ($local_logger:expr, $($arg:tt)+) => {{
+        $crate::log_with!($local_logger, log::info!($($arg)+));
+    }};
+}
+
+#[macro_export]
+macro_rules! log_warn_with {
+    ($local_logger:expr, $($arg:tt)+) => {{
+        $crate::log_with!($local_logger, log::warn!($($arg)+));
+    }};
+}
+
+#[macro_export]
+macro_rules! log_error_with {
+    ($local_logger:expr, $($arg:tt)+) => {{
+        $crate::log_with!($local_logger, log::error!($($arg)+));
+    }};
+}
+
+#[cfg(test)]
+mod tests {
+    use std::panic::catch_unwind;
+    use std::sync::{Arc, Mutex, MutexGuard};
+
+    use log::{Level, Log, Metadata, Record};
+
+    use crate::thread_local_logger::LocalLogger;
+
+    #[derive(Clone)]
+    struct VecLogs(Arc<Mutex<Vec<(Level, String)>>>);
+
+    impl VecLogs {
+        fn entries(&self) -> MutexGuard<Vec<(Level, String)>> {
+            self.0.lock().expect("Unlock should never fail")
+        }
+    }
+
+    impl Log for VecLogs {
+        fn enabled(&self, _: &Metadata) -> bool {
+            true
+        }
+
+        fn log(&self, record: &Record) {
+            self.entries()
+                .push((record.level(), record.args().to_string()))
+        }
+
+        fn flush(&self) {}
+    }
+
+    fn setup_logger() -> (LocalLogger, VecLogs) {
+        let logs = VecLogs(Arc::new(Mutex::new(vec![])));
+        crate::thread_local_logger::thread_init(logs.clone());
+        let local_logger = crate::thread_local_logger::get();
+        crate::thread_local_logger::reset();
+        (local_logger, logs)
+    }
+
+    #[test]
+    fn test_setup() {
+        setup_logger();
+        #[cfg(feature = "must-log")]
+        catch_unwind(|| log::info!("")).expect_err("Logger should not be set");
+    }
+
+    #[test]
+    fn test_log_with() {
+        let (logger, logs) = setup_logger();
+        log_with!(logger, log::info!("Dead beef {}", 1));
+        log_with!(logger, log::error!("Dead beef {}", 2));
+
+        assert_eq!(logs.entries().len(), 2);
+        assert_eq!(logs.entries()[0], (Level::Info, "Dead beef 1".to_owned()));
+        assert_eq!(logs.entries()[1], (Level::Error, "Dead beef 2".to_owned()));
+    }
+
+    #[test]
+    fn test_log_with_value() {
+        let (logger, logs) = setup_logger();
+        assert_eq!(log_with!(logger, 1), 1);
+        assert_eq!(log_with!(logger, "Dead beef"), "Dead beef");
+
+        assert_eq!(logs.entries().len(), 0);
+    }
+
+    #[test]
+    fn test_log_with_log() {
+        let (logger, logs) = setup_logger();
+        log_with!(logger, Level::Warn, "Dead beef {}", 3);
+        log_with!(logger, Level::Debug, "Dead beef {}", 4);
+
+        assert_eq!(logs.entries().len(), 2);
+        assert_eq!(logs.entries()[0], (Level::Warn, "Dead beef 3".to_owned()));
+        assert_eq!(logs.entries()[1], (Level::Debug, "Dead beef 4".to_owned()));
+    }
+
+    #[test]
+    fn test_log_wrappers() {
+        let (logger, logs) = setup_logger();
+        log_trace_with!(logger, "Dead beef {}", 5);
+        log_debug_with!(logger, "Dead beef {}", 6);
+        log_info_with!(logger, "Dead beef {}", 7);
+        log_warn_with!(logger, "Dead beef {}", 8);
+        log_error_with!(logger, "Dead beef {}", 9);
+
+        assert_eq!(logs.entries().len(), 5);
+        assert_eq!(logs.entries()[0], (Level::Trace, "Dead beef 5".to_owned()));
+        assert_eq!(logs.entries()[1], (Level::Debug, "Dead beef 6".to_owned()));
+        assert_eq!(logs.entries()[2], (Level::Info, "Dead beef 7".to_owned()));
+        assert_eq!(logs.entries()[3], (Level::Warn, "Dead beef 8".to_owned()));
+        assert_eq!(logs.entries()[4], (Level::Error, "Dead beef 9".to_owned()));
+    }
+}

+ 4 - 4
test_utils/src/thread_local_logger.rs

@@ -74,9 +74,7 @@ pub fn thread_init<T: 'static + Log>(logger: T) {
 
 #[doc(hidden)]
 pub fn get() -> LocalLogger {
-    let result = LOCAL_LOGGER.with(|inner| inner.borrow().clone());
-    let _ = result.deref();
-    result
+    LOCAL_LOGGER.with(|inner| inner.borrow().clone())
 }
 
 #[doc(hidden)]
@@ -92,7 +90,9 @@ pub fn reset() {
 impl LocalLogger {
     /// Inherit the logger from the current thread.
     pub fn inherit() -> Self {
-        get()
+        let result = get();
+        let _ = result.deref();
+        result
     }
 
     /// Set the logger of this thread to `self`.