async_logger_log 0.1.2

Asyncronous logging
Documentation
// general functional test

extern crate async_logger_log;

#[cfg(test)]
extern crate regex;

use async_logger_log::*;
use log::*;
use std::path::Path;
use std::io::{BufRead, BufReader};
use std::fs::File;
use std::thread;
use std::sync::{Once, Mutex, MutexGuard};
use std::mem::MaybeUninit;
use regex::Regex;


const LOG_DIR: &str = "/tmp/AsyncLoggerNBTest_49873205451691";

static mut TEST_MUTEX: MaybeUninit<Mutex<()>> = MaybeUninit::uninit();

static INIT_MUTEX: Once = Once::new();


fn prepare<'a>() -> MutexGuard<'a, ()> {

    INIT_MUTEX.call_once(|| {
        unsafe { TEST_MUTEX = MaybeUninit::new(Mutex::new(())) };
    });

    let mtx: &Mutex<()> = unsafe { TEST_MUTEX.as_ptr().as_ref().expect("Test mutex is not initialized") };
    let guard = mtx.lock().expect("Test mutex is poisoned");

    if Path::new(LOG_DIR).exists() {

        cleanup();
    }

    std::fs::create_dir(LOG_DIR).expect("Failed to create test dir");

    guard
}

fn cleanup() {
    std::fs::remove_dir_all(LOG_DIR).expect("Failed to delete test dir on cleanup");
}

fn get_resulting_file_path() -> String {

    String::from(Path::new(LOG_DIR)
        .read_dir()
        .expect("Failed to list files in test directory")
        .next()
        .expect("No files found in test directory")
        .expect("Failed to get entry inside test directory")
        .path()
        .to_str()
        .expect("Failed to get file path as str"))
}


#[test]
fn test_logger() {
    let write_line = "testing log record";
    let _guard = prepare();
    let cnt = 1000u32;
    let buf_sz = 1024;

    let mut matches = vec![];

    for s in &["main", "thread"] {
        for l in &["DEBUG", "ERROR", "INFO", "TRACE", "WARN"] {
            let re_str;
            #[cfg(feature="time")]
            {
                re_str = format!("\\[\\d{{4}}-\\d{{2}}-\\d{{2}} \\d{{2}}:\\d{{2}}:\\d{{2}}.\\d{{9}}[+-]\\d{{4}} {} {}\\]: {}\n",
                             l, s, write_line);
            }
            #[cfg(not(feature="time"))]
            {
                re_str = format!("\\[\\d+ {} {}\\]: {}\n",
                             l, s, write_line);
            }

            let re = Regex::new(&re_str).unwrap();

            matches.push((s, l, re, 0u32));
        }
    }


    let logger = Logger::new(LOG_DIR, buf_sz).expect("Failed to create Logger instance");
    log::set_boxed_logger(Box::new(logger)).expect("Failed to set logger");
    log::set_max_level(log::LevelFilter::Trace);

    let handle = thread::spawn(move || {
        for _ in 0..cnt {
            debug!(target: "thread", "{}", write_line);
            error!(target: "thread", "{}", write_line);
            info!(target: "thread", "{}", write_line);
            trace!(target: "thread", "{}", write_line);
            warn!(target: "thread", "{}", write_line);
            log!(target: "thread", Level::Error, "{}", write_line);
        }

        log::logger().flush();
    });

    for _ in 0..cnt {
        debug!(target: "main", "{}", write_line);
        error!(target: "main", "{}", write_line);
        info!(target: "main", "{}", write_line);
        trace!(target: "main", "{}", write_line);
        warn!(target: "main", "{}", write_line);
        log!(target: "main", Level::Error, "{}", write_line);
    }

    log::logger().flush();

    handle.join().expect("Failed on thread join");


    let out_file = get_resulting_file_path();

    let mut reader = BufReader::new(File::open(out_file).expect("Failed to open resulting file"));

    let mut line = String::new();

    loop {

        let len = reader.read_line(&mut line).expect("Failed to read line from the reslting file");

        if len == 0 {

            break;
        }

        for (_, _, re, n) in matches.iter_mut() {
            if re.is_match(&line) {
                *n += 1;
            }
        }

        line.clear();
    }

    for (thread, level, _, n) in matches.iter() {
        assert_eq!(
            cnt*(if **level == "ERROR" {2} else {1}), 
            *n, 
            "Line number mismatch for level {}, thread {}", level, thread
        );
    }

    cleanup();
}