spdlog/
log_crate_proxy.rs

1use std::time::SystemTime;
2
3use crate::{default_logger, sync::*, Logger, Record};
4
5/// Proxy layer for compatible [log crate].
6///
7/// Call [`init_log_crate_proxy`] to initialize the proxy, and then configure
8/// the proxy via [`log_crate_proxy`].
9///
10/// After the proxy is initialized, it will forward all log messages from `log`
11/// crate to the global default logger or the logger set by
12/// [`LogCrateProxy::set_logger`].
13///
14/// Note that the `log` crate uses a different log level filter and by default
15/// it rejects all log messages. To make `LogCrateProxy` able to receive log
16/// messages from `log` crate, you may need to call
17/// [`re_export::log::set_max_level`] with [`re_export::log::LevelFilter`].
18///
19/// ## Examples
20///
21/// ```
22/// use spdlog::re_export::log;
23///
24/// # fn main() -> Result<(), log::SetLoggerError> {
25/// spdlog::init_log_crate_proxy()?;
26/// // Enable all log messages from `log` crate.
27/// log::set_max_level(log::LevelFilter::Trace);
28/// # Ok(()) }
29/// ```
30///
31/// For more and detailed examples, see [./examples] directory.
32///
33/// [log crate]: https://crates.io/crates/log
34/// [`init_log_crate_proxy`]: crate::init_log_crate_proxy
35/// [`log_crate_proxy`]: crate::log_crate_proxy()
36/// [`re_export::log::set_max_level`]: crate::re_export::log::set_max_level
37/// [`re_export::log::LevelFilter`]: crate::re_export::log::LevelFilter
38/// [./examples]: https://github.com/SpriteOvO/spdlog-rs/tree/main/spdlog/examples
39#[derive(Default)]
40pub struct LogCrateProxy {
41    logger: ArcSwapOption<Logger>,
42}
43
44impl LogCrateProxy {
45    #[must_use]
46    pub(crate) fn new() -> Self {
47        Self::default()
48    }
49
50    /// Sets a logger as the new receiver, and returens the old one.
51    ///
52    /// If the argument `logger` is `None`, the global default logger will be
53    /// used.
54    pub fn swap_logger(&self, logger: Option<Arc<Logger>>) -> Option<Arc<Logger>> {
55        self.logger.swap(logger)
56    }
57
58    /// Sets a logger as the new receiver.
59    ///
60    /// If the argument `logger` is `None`, the global default logger will be
61    /// used.
62    pub fn set_logger(&self, logger: Option<Arc<Logger>>) {
63        self.swap_logger(logger);
64    }
65
66    #[must_use]
67    fn logger(&self) -> Arc<Logger> {
68        self.logger.load_full().unwrap_or_else(default_logger)
69    }
70}
71
72impl log::Log for LogCrateProxy {
73    fn enabled(&self, metadata: &log::Metadata) -> bool {
74        self.logger().should_log(metadata.level().into())
75    }
76
77    fn log(&self, record: &log::Record) {
78        let logger = self.logger();
79        let record = Record::from_log_crate_record(&logger, record, SystemTime::now());
80        logger.log(&record)
81    }
82
83    fn flush(&self) {
84        self.logger().flush()
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use crate::test_utils::*;
92
93    #[test]
94    fn proxy() {
95        crate::init_log_crate_proxy().unwrap();
96        log::set_max_level(log::LevelFilter::Debug);
97
98        let sink = Arc::new(TestSink::new());
99        crate::log_crate_proxy()
100            .set_logger(Some(Arc::new(build_test_logger(|b| b.sink(sink.clone())))));
101
102        assert_eq!(sink.log_count(), 0);
103
104        log::info!("hello");
105        log::error!("world");
106
107        assert_eq!(sink.log_count(), 2);
108        assert_eq!(
109            sink.payloads(),
110            vec!["hello".to_string(), "world".to_string()]
111        );
112    }
113}