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}