libmultilog 0.1.0

Various logging implementations in Rust
Documentation
extern crate libmultilog;
#[macro_use]
extern crate log;
#[cfg(feature = "mysql")]
extern crate mysql;
#[cfg(feature = "rusqlite")]
extern crate rusqlite;
extern crate time;

use libmultilog::multi::*;
use log::{LogLevelFilter, LogRecord};
#[cfg(feature = "mysql")]
use mysql::conn::MyOpts;
#[cfg(feature = "mysql")]
use mysql::conn::pool::MyPool;
#[cfg(feature = "rusqlite")]
use rusqlite::SqliteConnection;
use std::default::Default;
use std::env;
use std::fs::{self, File};
use std::io::{BufWriter, Write};
#[cfg(feature = "socket")]
use std::io::Read;
#[cfg(feature = "socket")]
use std::net::{TcpListener, TcpStream};
use std::sync::mpsc::{channel, Sender, Receiver};
#[cfg(feature = "socket")]
use std::thread;

fn stdoutfn(record: &LogRecord) {
    println!("{}", record.args());
}

fn fileoutfn(record: &LogRecord, w: &mut BufWriter<File>) {
    let now = time::now();
    w.write_fmt(format_args!("{} {:5} {:4} -- {}: {}\n",
                             now.rfc3339(),
                             record.level(),
                             record.location().line(),
                             record.location().module_path(),
                             record.args()))
     .and(w.flush())
     .unwrap();
}

#[cfg(feature = "mysql")]
fn mysqloutfn(record: &LogRecord, pool: &mut MyPool) {
    let mut stmt = pool.prepare(r"INSERT INTO log
                                 (created, level, line, module, message)
                                 VALUES (?, ?, ?, ?, ?)")
                       .unwrap();

    match stmt.execute((&time::get_time(),
                        &format!("{}", record.level()),
                        &format!("{}", record.location().line()),
                        &record.location().module_path(),
                        &format!("{}", record.args()))) {
        Ok(_) => {}
        Err(e) => panic!("Unable to insert log record! {}", e),
    };
}

#[cfg(feature = "socket")]
fn socketoutfn(record: &LogRecord, w: &mut BufWriter<TcpStream>) {
    let now = time::now();
    w.write_fmt(format_args!("{} {:5} {:4} -- {}: {}\n",
                             now.rfc3339(),
                             record.level(),
                             record.location().line(),
                             record.location().module_path(),
                             record.args()))
     .and(w.flush())
     .unwrap();
}

#[cfg(feature = "rusqlite")]
fn sqliteoutfn(record: &LogRecord, conn: &mut SqliteConnection) {
    conn.execute(r"INSERT INTO log
                  (created, level, line, module, message)
                  VALUES ($1, $2, $3, $4, $5)",
                 &[&time::get_time(),
                   &format!("{}", record.level()),
                   &format!("{}", record.location().line()),
                   &record.location().module_path(),
                   &format!("{}", record.args())])
        .unwrap();
}

#[cfg(feature = "mysql")]
fn config_mysql(ml: &mut MultiLogger) -> &mut MultiLogger {
    ml.enable_mysql(mysqloutfn,
                    MyOpts {
                        user: Some("travis".to_string()),
                        pass: Some("".to_string()),
                        db_name: Some("log_test".to_string()),
                        ..Default::default()
                    },
                    true);
    ml
}

#[cfg(not(feature = "mysql"))]
fn config_mysql(ml: &mut MultiLogger) -> &mut MultiLogger {
    ml
}

#[cfg(feature = "mysql")]
fn test_mysql() {
    // Read the mysql database row
    let pool = MyPool::new(MyOpts {
                   user: Some("travis".to_string()),
                   pass: Some("".to_string()),
                   db_name: Some("log_test".to_string()),
                   ..Default::default()
               })
                   .unwrap();

    let _ = pool.prep_exec("SELECT * FROM log", ()).map(|result| {
        for row in result {
            assert_eq!("'TEST'", &row.unwrap()[5].into_str()[..]);
        }
    });
}

#[cfg(not(feature = "mysql"))]
fn test_mysql() {}

#[cfg(feature = "socket")]
fn config_socket(ml: &mut MultiLogger, tx: Sender<(usize, [u8; 256])>) -> &mut MultiLogger {
    // Setup a temporary tcp listener for the socket test
    let listener = TcpListener::bind("127.0.0.1:10663").unwrap();
    let mut lbuf = [0u8; 256];

    // Accept the connection on another thread.
    thread::spawn(move || {
        let (mut stream, _) = listener.accept().unwrap();
        let l = stream.read(&mut lbuf).unwrap();
        let _ = tx.send((l, lbuf)).unwrap();
    });

    ml.enable_socket(socketoutfn, "127.0.0.1:10663");
    ml
}

#[cfg(not(feature = "socket"))]
fn config_socket(ml: &mut MultiLogger, _: Sender<()>) -> &mut MultiLogger {
    ml
}

#[cfg(feature = "socket")]
fn test_socket(rx: Receiver<(usize, [u8; 256])>) {
    // Wait for the socket result
    let (l, buf) = rx.recv().unwrap();
    let s = String::from_utf8_lossy(&buf[..l]);
    let toks = s.split(' ');
    let lt = toks.last().unwrap();
    assert_eq!("TEST", &(lt.trim())[..]);
}

#[cfg(not(feature = "socket"))]
fn test_socket(_: Receiver<()>) {}

#[cfg(feature = "rusqlite")]
fn config_sqlite(ml: &mut MultiLogger) -> &mut MultiLogger {
    let tmp = env::temp_dir();
    let tmp_db = tmp.join("log.db");
    ml.enable_sqlite(sqliteoutfn, Some(tmp_db), true);
    ml
}

#[cfg(not(feature = "rusqlite"))]
fn config_sqlite(ml: &mut MultiLogger) -> &mut MultiLogger {
    ml
}

#[cfg(feature = "rusqlite")]
fn test_sqlite() {
    // Read the sqlite database row
    let tmp = env::temp_dir();
    let tmp_db = tmp.join("log.db");
    let conn = SqliteConnection::open(&tmp_db).unwrap();

    let mut stmt = conn.prepare("SELECT * FROM log").unwrap();
    for row in stmt.query(&[]).unwrap().map(|row| row.unwrap()) {
        let msg: String = row.get(5);
        assert_eq!("TEST", &msg[..]);
    }

    let _ = fs::remove_file(tmp_db).unwrap();
}

#[cfg(not(feature = "rusqlite"))]
fn test_sqlite() {}

#[test]
fn test_chained_loggers() {
    // Setup the file and logging db files.
    let tmp = env::temp_dir();
    let tmp_log = tmp.join("log.log");

    let (tx, rx) = channel();

    // Initialize all the loggers
    let mut ml: MultiLogger = Default::default();
    ml.enable_stdout(stdoutfn);
    ml.enable_file(fileoutfn, tmp_log);

    config_mysql(&mut ml);
    config_socket(&mut ml, tx);
    config_sqlite(&mut ml);
    assert!(init_multi_logger(LogLevelFilter::Debug, ml).is_ok());

    // Log a message...
    error!("TEST");

    test_mysql();
    test_socket(rx);
    test_sqlite();

    // Cleanup
    let _ = fs::remove_file(tmp.join("log.log")).unwrap();
}