libmultilog 0.1.0

Various logging implementations in Rust
Documentation
//! Multi Logging Implementation
use file::FileLogger;
use log::{self, Log, LogLevel, LogLevelFilter, LogMetadata, LogRecord, SetLoggerError};
#[cfg(feature = "mysql")]
use mysql::conn::MyOpts;
#[cfg(feature = "mysql")]
use mysql::conn::pool::MyPool;
#[cfg(feature = "mysql")]
use mysqll::MysqlLogger;
use regex::Regex;
#[cfg(feature = "rusqlite")]
use rusqlite::SqliteConnection;
#[cfg(feature = "socket")]
use socket::SocketLogger;
#[cfg(feature = "rusqlite")]
use sqlite::SqliteLogger;
use stdout::StdoutLogger;
use std::fs::File;
use std::io::BufWriter;
#[cfg(feature = "socket")]
use std::net::{TcpStream, ToSocketAddrs};
use std::path::PathBuf;

bitflags! {
    #[derive(Default)]
    /// Logger Bitflags
    flags Loggers: u32 {
        /// StdoutLogger
        const STDOUT = 0b00000001,
        /// FileLogger
        const FILE   = 0b00000010,
        /// SocketLogger
        #[cfg(feature = "socket")] const SOCKET = 0b00000100,
        /// SqliteLogger
        #[cfg(feature = "rusqlite")] const SQLITE = 0b00001000,
        /// MysqlLogger
        #[cfg(feature = "mysql")] const MYSQL = 0b00100000,
        /// Local Loggers
        const LOCAL  = STDOUT.bits | FILE.bits,
    }
}

#[derive(Default)]
/// Structure that holds multiple loggers.
pub struct MultiLogger {
    loggers: Loggers,
    file: Option<FileLogger>,
    stdout: Option<StdoutLogger>,
    #[cfg(feature = "socket")]
    socket: Option<SocketLogger>,
    #[cfg(feature = "rusqlite")]
    sqlite: Option<SqliteLogger>,
    #[cfg(feature = "mysql")]
    mysql: Option<MysqlLogger>,
}

impl MultiLogger {
    /// Enable file logging.
    pub fn enable_file(&mut self,
                       ofn: fn(&LogRecord, &mut BufWriter<File>),
                       path: PathBuf)
                       -> &mut MultiLogger {
        self.loggers.toggle(FILE);
        self.file = Some(FileLogger::new(path, ofn));
        self
    }

    /// Add a file filter.
    pub fn add_file_filter(&mut self, lvl: LogLevel, r: Regex) -> &mut MultiLogger {
        match self.file {
            Some(ref mut f) => {
                f.add_filter(lvl, r);
            }
            None => {}
        }
        self
    }

    #[cfg(feature = "mysql")]
    /// Enable MysqlLogger.
    pub fn enable_mysql(&mut self,
                        ofn: fn(&LogRecord, &mut MyPool),
                        opts: MyOpts,
                        create: bool)
                        -> &mut MultiLogger {
        self.loggers.toggle(MYSQL);
        self.mysql = Some(MysqlLogger::new(opts, create, ofn));
        self
    }

    #[cfg(feature = "socket")]
    /// Enable SocketLogger.
    pub fn enable_socket<A: ToSocketAddrs>(&mut self,
                                           ofn: fn(&LogRecord, &mut BufWriter<TcpStream>),
                                           addr: A)
                                           -> &mut MultiLogger {
        self.loggers.toggle(SOCKET);
        self.socket = Some(SocketLogger::new(addr, ofn));
        self
    }

    #[cfg(feature = "rusqlite")]
    /// Enable SqliteLogger.
    pub fn enable_sqlite(&mut self,
                         ofn: fn(&LogRecord, &mut SqliteConnection),
                         path: Option<PathBuf>,
                         create: bool)
                         -> &mut MultiLogger {
        self.loggers.toggle(SQLITE);
        self.sqlite = Some(SqliteLogger::new(path, create, ofn));
        self
    }

    /// Enable stdout logging.
    pub fn enable_stdout(&mut self, ofn: fn(&LogRecord)) -> &mut MultiLogger {
        self.loggers.toggle(STDOUT);
        self.stdout = Some(StdoutLogger::new(ofn));
        self
    }
}

#[cfg(feature = "mysql")]
fn log_mysql(ml: &MultiLogger, record: &LogRecord) {
    if ml.loggers.contains(MYSQL) {
        match ml.mysql {
            Some(ref s) => {
                s.log(record);
            }
            None => {}
        }
    }
}

#[cfg(not(feature = "mysql"))]
fn log_mysql(_: &MultiLogger, _: &LogRecord) {}

#[cfg(feature = "socket")]
fn log_socket(ml: &MultiLogger, record: &LogRecord) {
    if ml.loggers.contains(SOCKET) {
        match ml.socket {
            Some(ref s) => {
                s.log(record);
            }
            None => {}
        }
    }
}

#[cfg(not(feature = "socket"))]
fn log_socket(_: &MultiLogger, _: &LogRecord) {}

#[cfg(feature = "rusqlite")]
fn log_sqlite(ml: &MultiLogger, record: &LogRecord) {
    if ml.loggers.contains(SQLITE) {
        match ml.sqlite {
            Some(ref s) => {
                s.log(record);
            }
            None => {}
        }
    }
}

#[cfg(not(feature = "rusqlite"))]
fn log_sqlite(_: &MultiLogger, _: &LogRecord) {}

impl log::Log for MultiLogger {
    fn enabled(&self, _: &LogMetadata) -> bool {
        true
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            if self.loggers.contains(FILE) {
                match self.file {
                    Some(ref f) => {
                        f.log(record);
                    }
                    None => {}
                }
            }

            log_mysql(&self, record);
            log_socket(&self, record);
            log_sqlite(&self, record);

            if self.loggers.contains(STDOUT) {
                match self.stdout {
                    Some(ref s) => {
                        s.log(record);
                    }
                    None => {}
                }
            }
        }
    }
}

/// Initialize the multi logger.
pub fn init_multi_logger(lvl: LogLevelFilter, ml: MultiLogger) -> Result<(), SetLoggerError> {
    log::set_logger(|max_log_level| {
        max_log_level.set(lvl);
        Box::new(ml)
    })
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    #[cfg(feature = "mysql")]
    fn test_mysql_bitflag() {
        let mut loggers = MYSQL;
        assert_eq!(loggers.bits, 32);
        loggers.toggle(MYSQL);
        assert_eq!(loggers.bits, 0);
    }

    #[test]
    #[cfg(feature = "socket")]
    fn test_socket_bitflag() {
        let mut loggers = SOCKET;
        assert_eq!(loggers.bits, 4);
        loggers.toggle(SOCKET);
        assert_eq!(loggers.bits, 0);
    }

    #[test]
    #[cfg(feature = "rusqlite")]
    fn test_sqlite_bitflag() {
        let mut loggers = SQLITE;
        assert_eq!(loggers.bits, 8);
        loggers.toggle(SQLITE);
        assert_eq!(loggers.bits, 0);
    }

    #[test]
    fn test_file_bitflag() {
        let mut loggers = FILE;
        assert_eq!(loggers.bits, 2);
        loggers.toggle(FILE);
        assert_eq!(loggers.bits, 0);
    }

    #[test]
    fn test_stdout_bitflag() {
        let mut loggers = STDOUT;
        assert_eq!(loggers.bits, 1);
        loggers.toggle(STDOUT);
        assert_eq!(loggers.bits, 0);
    }

    #[test]
    fn test_loggers_bitflags() {
        let mut loggers = FILE | STDOUT;
        assert_eq!(loggers.bits, 3);
        loggers.toggle(STDOUT);
        assert_eq!(loggers.bits, 2);
        loggers.toggle(FILE);
        loggers.toggle(STDOUT);
        assert_eq!(loggers.bits, 1);
        loggers.toggle(STDOUT);
        assert_eq!(loggers.bits, 0);
        loggers = LOCAL;
        assert_eq!(loggers.bits, 3);
    }
}