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
//! Have multiple loggers implementing [`log::Log`] at once.

use std::sync::atomic::{AtomicBool, Ordering::SeqCst};

static MULTI_LOGGER: MultiLogger = MultiLogger::new();

static HAS_MULTI_LOGGER: AtomicBool = AtomicBool::new(false);

/// Produced when trying to install additional loggers when [`crate::setup_logging`] has not been called.
///
/// This can happen for example when users of the `rerun` crate use the `spawn` method,
/// and they aren't using `re_log`.
#[derive(Clone, Copy, Debug)]
pub struct MultiLoggerNotSetupError {}

/// Install the multi-logger as the default logger.
pub fn init() -> Result<(), log::SetLoggerError> {
    HAS_MULTI_LOGGER.store(true, SeqCst);
    log::set_logger(&MULTI_LOGGER)
}

/// Install an additional global logger.
pub fn add_boxed_logger(logger: Box<dyn log::Log>) -> Result<(), MultiLoggerNotSetupError> {
    add_logger(Box::leak(logger))
}

/// Install an additional global logger.
pub fn add_logger(logger: &'static dyn log::Log) -> Result<(), MultiLoggerNotSetupError> {
    if HAS_MULTI_LOGGER.load(SeqCst) {
        MULTI_LOGGER.loggers.write().push(logger);
        Ok(())
    } else {
        Err(MultiLoggerNotSetupError {})
    }
}

/// Forward log messages to multiple [`log::log`] receivers.
struct MultiLogger {
    loggers: parking_lot::RwLock<Vec<&'static dyn log::Log>>,
}

impl MultiLogger {
    pub const fn new() -> Self {
        Self {
            loggers: parking_lot::RwLock::new(vec![]),
        }
    }
}

impl log::Log for MultiLogger {
    fn enabled(&self, metadata: &log::Metadata<'_>) -> bool {
        self.loggers
            .read()
            .iter()
            .any(|logger| logger.enabled(metadata))
    }

    fn log(&self, record: &log::Record<'_>) {
        for logger in self.loggers.read().iter() {
            logger.log(record);
        }
    }

    fn flush(&self) {
        for logger in self.loggers.read().iter() {
            logger.flush();
        }
    }
}