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
122
123
124
// Copyright 2016 Victor Brekenfeld
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! Module providing the CombinedLogger Implementation

use crate::{Config, SharedLogger};
use log::{set_boxed_logger, set_max_level, LevelFilter, Log, Metadata, Record, SetLoggerError};

/// The CombinedLogger struct. Provides a Logger implementation that proxies multiple Loggers as one.
///
/// The purpose is to allow multiple Loggers to be set globally
pub struct CombinedLogger {
    level: LevelFilter,
    logger: Vec<Box<dyn SharedLogger>>,
}

impl CombinedLogger {
    /// init function. Globally initializes the CombinedLogger as the one and only used log facility.
    ///
    /// Takes all used Loggers as a Vector argument. None of those Loggers should already be set globally.
    ///
    /// All loggers need to implement `log::Log` and `logger::SharedLogger` and need to provide a way to be
    /// initialized without calling `set_logger`. All loggers of this library provide a `new(..)`` method
    /// for that purpose.
    /// Fails if another logger is already set globally.
    ///
    /// # Examples
    /// ```
    /// # extern crate simplelog;
    /// # use simplelog::*;
    /// # use std::fs::File;
    /// # fn main() {
    /// let _ = CombinedLogger::init(
    ///             vec![
    /// #               #[cfg(feature = "term")]
    ///                 TermLogger::new(LevelFilter::Info, Config::default(), TerminalMode::Mixed).unwrap(),
    ///                 WriteLogger::new(LevelFilter::Info, Config::default(), File::create("my_rust_bin.log").unwrap())
    ///             ]
    ///         );
    /// # }
    /// ```
    pub fn init(logger: Vec<Box<dyn SharedLogger>>) -> Result<(), SetLoggerError> {
        let comblog = CombinedLogger::new(logger);
        set_max_level(comblog.level());
        set_boxed_logger(comblog)
    }

    /// allows to create a new logger, that can be independently used, no matter whats globally set.
    ///
    /// no macros are provided for this case and you probably
    /// dont want to use this function, but `init()`, if you dont want to build a `CombinedLogger`.
    ///
    /// Takes all used loggers as a Vector argument. The log level is automatically determined by the
    /// lowest log level used by the given loggers.
    ///
    /// All loggers need to implement log::Log.
    ///
    /// # Examples
    /// ```
    /// # extern crate simplelog;
    /// # use simplelog::*;
    /// # use std::fs::File;
    /// # fn main() {
    /// let combined_logger = CombinedLogger::new(
    ///             vec![
    /// #               #[cfg(feature = "term")]
    ///                 TermLogger::new(LevelFilter::Debug, Config::default(), TerminalMode::Mixed).unwrap(),
    ///                 WriteLogger::new(LevelFilter::Info, Config::default(), File::create("my_rust_bin.log").unwrap())
    ///             ]
    ///         );
    /// # }
    /// ```
    pub fn new(logger: Vec<Box<dyn SharedLogger>>) -> Box<CombinedLogger> {
        let mut log_level = LevelFilter::Off;
        for log in &logger {
            if log_level < log.level() {
                log_level = log.level();
            }
        }

        Box::new(CombinedLogger {
            level: log_level,
            logger: logger,
        })
    }
}

impl Log for CombinedLogger {
    fn enabled(&self, metadata: &Metadata<'_>) -> bool {
        metadata.level() <= self.level
    }

    fn log(&self, record: &Record<'_>) {
        if self.enabled(record.metadata()) {
            for log in &self.logger {
                log.log(record);
            }
        }
    }

    fn flush(&self) {
        for log in &self.logger {
            log.flush();
        }
    }
}

impl SharedLogger for CombinedLogger {
    fn level(&self) -> LevelFilter {
        self.level
    }

    fn config(&self) -> Option<&Config> {
        None
    }

    fn as_log(self: Box<Self>) -> Box<dyn Log> {
        Box::new(*self)
    }
}