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
105
106
extern crate env_logger;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate clap;
#[cfg(feature = "color")]
extern crate ansi_term;

#[cfg(feature = "color")]
use ansi_term::Colour::{Blue, Green, Red, White, Yellow};
use clap::ArgMatches;
use env_logger::LogBuilder;
use log::{LogLevel, LogLevelFilter, LogRecord, SetLoggerError};
use std::sync::atomic::{AtomicUsize, Ordering};

lazy_static! {
    static ref HACKLOG_INDENT:AtomicUsize = {
        AtomicUsize::new(0)
    };
}


pub fn init(opts: Option<&ArgMatches>, level: Option<LogLevelFilter>) -> Result<(), SetLoggerError> {
    let mut builder = LogBuilder::new();
    init_format(&mut builder);
    if let Some(opts) = opts {
        init_clap(opts, &mut builder);
    }
    if let Some(level) = level {
        builder.filter(None, level);
    }
    builder.init()
}

fn init_format(builder: &mut LogBuilder) {
    #[cfg(not(feature = "color"))]
    builder.format(|record: &LogRecord| {
        format!("{:>ident$} {}",
                match record.level() {
                    LogLevel::Error => "[-]",
                    LogLevel::Warn => "[*]",
                    LogLevel::Info => "[+]",
                    LogLevel::Debug => "[#]",
                    LogLevel::Trace => "[%]",
                },
                record.args(),
                ident=HACKLOG_INDENT.load(Ordering::SeqCst) * 4)
    });
    #[cfg(feature = "color")]
    builder.format(|record: &LogRecord| {
        format!("{holder:>ident$}{} {}",
                match record.level() {
                    LogLevel::Error => Red.paint("[-]"),
                    LogLevel::Warn => Yellow.paint("[*]"),
                    LogLevel::Info => Green.paint("[+]"),
                    LogLevel::Debug => Blue.paint("[#]"),
                    LogLevel::Trace => White.paint("[%]"),
                },
                record.args(),
                holder="",
                ident=HACKLOG_INDENT.load(Ordering::SeqCst) * 4)
    });
}

fn init_clap(opts: &ArgMatches, builder: &mut LogBuilder) {
    builder.filter(None,
                   match opts.occurrences_of("verbose") {
                       n if n >= 3 => LogLevelFilter::Trace,
                       n if n == 2 => LogLevelFilter::Debug,
                       n if n == 1 => LogLevelFilter::Info,
                       _ => LogLevelFilter::Warn,
                   });
}

fn indent() -> usize {
    HACKLOG_INDENT.fetch_add(1, Ordering::SeqCst)
}

fn unindent() -> usize {
    HACKLOG_INDENT.fetch_sub(1, Ordering::SeqCst)
}

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

    #[test]
    fn test() {
        init(None, Some(LogLevelFilter::Trace)).unwrap();
        error!("level 1 - error message 1");
        indent();
        error!("level 2 - error message 1");
        indent();
        error!("level 3 - error message 1");
        unindent();
        error!("level 2 - error message 2");
        unindent();
        error!("level 1 - error message 1");
        warn!("a warning message");
        info!("a info message");
        debug!("a debug message");
        trace!("a trace message");
    }
}