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

use fern::Dispatch;
use std::path::Path;
use std::fs::{File, create_dir, OpenOptions};
use std::fmt::Display;
use colored::*;
use crate::{error::Error, Result};
pub use log::{Level, LevelFilter};

#[derive(Copy, Clone)]
pub struct Settings {
    pub level: LevelFilter,
    pub everything: bool,
}

pub fn get_log_file (path: &Path) -> Result<File> {
    let now = ::chrono::Local::now().format("%Y_%m_%d__%H_%M_%S");

    if path.exists() {
        if !path.is_dir() {
            return Err(Error::from(format!("{:?} is not a directory", path)));
        }
    } else {
        create_dir(path).map_err(|e| Error::from(format!("could not create directory {:?}: {}", path, e)))?;
    }

    let mut path_buf = path.to_path_buf();
    path_buf.push(&now.to_string());
    path_buf.set_extension("log");

    OpenOptions::new()
        .create(true)
        .write(true)
        .open(&path_buf)
        .map_err(|e| Error::from(format!("could not log to {:?}: {}", &path_buf, e)))
}

pub fn init (path: &Path, settings: Settings) {

    let colors = ::fern::colors::ColoredLevelConfig::new()
        .trace(Color::Blue)
        .debug(Color::BrightBlue)
        .info(Color::BrightGreen)
        .warn(Color::BrightYellow)
        .error(Color::BrightRed);

    fn white <T: Display> (msg: T, colored: bool) -> String {
        if colored {
            format!("{}", format!("{}", msg).white())
        } else {
            format!("{}", msg)
        }   
    }

    macro_rules! format_msg {
        ($colored:expr) => {
            move |out, message, record| {
                out.finish(format_args!(
                    "{} {}:{} {}",
                    white(::chrono::Local::now().format("%Y-%m-%d %H:%M:%S"), $colored),
                    if $colored {
                        format!("{}", colors.color(record.level()))
                    } else {
                        format!("{}", record.level())
                    },
                    if settings.everything {
                        white(format!(" {}", record.target()), $colored)
                    } else { "".to_owned() },
                    message
                ))
            }
        }
    }

    // CMD Logging (colored, with user specified level)
    let mut dispatch = Dispatch::new()
        .filter(move |metadata| {
            settings.everything || &metadata.target()[0..9] == "torchbear"
        })
        .level(settings.level)
        .chain(Dispatch::new()
            .format( format_msg!(true) )
            .chain(::std::io::stdout())
        );

    // File Logging (uncolored, only info or worse)
    match ::std::fs::create_dir_all(path) {
        Err(err) => error!("{}", err),
        _ => match get_log_file(path) {
            Ok(file) => {
                dispatch = dispatch.chain(Dispatch::new()
                    .format( format_msg!(false) )
                    .chain(file)
                );
            },
            Err(err) => error!("{}", err)
        }
    };


    if dispatch.apply().is_err() {
        panic!("A logger instance was already set");
    }
}