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
107
108
109
110
111
112

use log::{Level, LevelFilter, Log, Metadata, Record, SetLoggerError};

use chrono::{Local, Utc};
use std::path::Path;
use ansi_term::Color;

pub enum Timezone {
    None,
    Utc,
    Local,
}

static LOG_LEVEL_NAMES: [&str; 6] = ["N/A", "E", "W", "I", "D", "T"];

fn level_to_str(l: &Level) -> &'static str {
    LOG_LEVEL_NAMES[*l as usize]
}

static DEFAULT_TIME_FORMAT: &str = "%Y-%m-%d %H:%M:%S";

pub struct XLog {
    level: LevelFilter,
    timezone: Timezone,
    colored: bool,
}

impl XLog {
    #[must_use = "You must call init() to begin logging"]
    pub fn new() -> XLog {
        XLog {
            level: LevelFilter::Trace,
            timezone: Timezone::Local,
            colored: true,
        }
    }

    pub fn with_level(mut self, level: LevelFilter) -> XLog {
        self.level = level;
        self
    }

    pub fn with_timezone(mut self, timezone: Timezone) -> XLog {
        self.timezone = timezone;
        self
    }

    pub fn with_color(mut self, colored: bool) -> XLog {
        self.colored = colored;
        self
    }

    pub fn init(self) -> Result<(), SetLoggerError> {
        log::set_max_level(log::STATIC_MAX_LEVEL);
        log::set_boxed_logger(Box::new(self))?;
        Ok(())
    }
}

impl Default for XLog {
    fn default() -> Self {
        XLog::new()
    }
}

impl Log for XLog {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level().to_level_filter() <= self.level
    }

    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {

            let timestamp = {
                match self.timezone{
                    Timezone::None => "".to_string(),
                    Timezone::Local => format!("{} ", Local::now().format(DEFAULT_TIME_FORMAT)),
                    Timezone::Utc => format!("{} ", Utc::now().format(DEFAULT_TIME_FORMAT)),
                }
            };

            let path = Path::new(record.file().unwrap_or("???"));
            let pathname = path.file_stem().unwrap().to_owned().into_string().unwrap();
            let mut pathname = format!("{}:{}", pathname, record.line().unwrap_or(0));

            let level_string = level_to_str(&record.level());

            let mut color_msg = format!("[{}]: {}", level_string, record.args());

            if self.colored {
                color_msg = match record.level() {
                    Level::Error => Color::Red.paint(color_msg).to_string(),
                    Level::Warn => Color::Yellow.paint(color_msg).to_string(),
                    Level::Info => Color::Green.paint(color_msg).to_string(),
                    Level::Debug => Color::Blue.paint(color_msg).to_string(),
                    Level::Trace => Color::White.paint(color_msg).to_string(),
                };
                pathname = Color::Blue.paint(pathname).to_string();
            }

            let message = format!("{}{} {}", timestamp, pathname, color_msg);

            #[cfg(not(feature = "stderr"))]
            println!("{}", message);

            #[cfg(feature = "stderr")]
            eprintln!("{}", message);
        }
    }

    fn flush(&self) {}
}