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)]
flags Loggers: u32 {
const STDOUT = 0b00000001,
const FILE = 0b00000010,
#[cfg(feature = "socket")] const SOCKET = 0b00000100,
#[cfg(feature = "rusqlite")] const SQLITE = 0b00001000,
#[cfg(feature = "mysql")] const MYSQL = 0b00100000,
const LOCAL = STDOUT.bits | FILE.bits,
}
}
#[derive(Default)]
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 {
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
}
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")]
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")]
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")]
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
}
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 => {}
}
}
}
}
}
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);
}
}