1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
use std::time::SystemTime;
/// Re-export some stuff from `log` crate for convenience.
///
/// Users sometimes need these stuff, re-exporting them eliminates the need to
/// explicitly depend on `log` crate in `Cargo.toml`.
///
/// See the documentation of [`LogCrateProxy`].
#[cfg(feature = "log")] // Intentionally redundant, workaround for defects in nested exports of feature
// `doc_auto_cfg`.
pub mod log_crate {
pub use log::{set_max_level, LevelFilter, SetLoggerError};
}
use crate::{default_logger, sync::*, Logger, Record};
/// Log crate proxy.
///
/// It forwards all log messages from `log` crate to [`default_logger`] by
/// default, and you can set a separate logger for it via
/// [`LogCrateProxy::set_logger`].
///
/// If upstream dependencies use `log` crate to output log messages, they may
/// also be received by `LogCrateProxy`.
///
/// Note that the `log` crate uses a different log level filter and by default
/// it rejects all log messages. To make `LogCrateProxy` able to receive log
/// messages from `log` crate, you may need to call [`log_crate::set_max_level`]
/// with [`log_crate::LevelFilter`].
///
/// ## Examples
///
/// ```
/// use spdlog::log_crate as log;
///
/// # fn main() -> Result<(), log::SetLoggerError> {
/// spdlog::init_log_crate_proxy()?;
/// // Enable all log messages from `log` crate.
/// log::set_max_level(log::LevelFilter::Trace);
/// # Ok(()) }
/// ```
///
/// For more and detailed examples, see [./examples] directory.
///
/// [./examples]: https://github.com/SpriteOvO/spdlog-rs/tree/main/spdlog/examples
#[derive(Default)]
pub struct LogCrateProxy {
logger: ArcSwapOption<Logger>,
}
impl LogCrateProxy {
#[must_use]
pub(crate) fn new() -> Self {
Self::default()
}
/// Swaps a logger.
///
/// If the argument `logger` is `None`, the return value of
/// [`default_logger`] will be used.
pub fn swap_logger(&self, logger: Option<Arc<Logger>>) -> Option<Arc<Logger>> {
self.logger.swap(logger)
}
/// Sets a logger.
///
/// If the argument `logger` is `None`, the return value of
/// [`default_logger`] will be used.
pub fn set_logger(&self, logger: Option<Arc<Logger>>) {
self.swap_logger(logger);
}
#[must_use]
fn logger(&self) -> Arc<Logger> {
self.logger.load_full().unwrap_or_else(default_logger)
}
}
impl log::Log for LogCrateProxy {
fn enabled(&self, metadata: &log::Metadata) -> bool {
self.logger().should_log(metadata.level().into())
}
fn log(&self, record: &log::Record) {
let logger = self.logger();
let record = Record::from_log_crate_record(&logger, record, SystemTime::now());
logger.log(&record)
}
fn flush(&self) {
self.logger().flush()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::*;
#[test]
fn proxy() {
crate::init_log_crate_proxy().unwrap();
log::set_max_level(log::LevelFilter::Debug);
let sink = Arc::new(CounterSink::new());
crate::log_crate_proxy().set_logger(Some(Arc::new(
test_logger_builder().sink(sink.clone()).build().unwrap(),
)));
assert_eq!(sink.log_count(), 0);
log::info!("hello");
log::error!("world");
assert_eq!(sink.log_count(), 2);
assert_eq!(
sink.payloads(),
vec!["hello".to_string(), "world".to_string()]
);
}
}