summaryrefslogtreecommitdiffstats
path: root/src/logger.rs
blob: da8e31b88f473b84ef77ec000983d20402804433 (plain)
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
use ansi_term::Color;
use log::{Level, LevelFilter, Metadata, Record};
use once_cell::sync::OnceCell;
use std::{
    collections::HashSet,
    env,
    fs::{self, File, OpenOptions},
    io::Write,
    path::PathBuf,
    sync::Mutex,
};

pub struct StarshipLogger {
    log_file: OnceCell<Mutex<File>>,
    log_file_path: PathBuf,
    log_file_content: HashSet<String>,
    log_level: Level,
}

impl Default for StarshipLogger {
    fn default() -> Self {
        let log_dir = env::var_os("STARSHIP_CACHE")
            .map(PathBuf::from)
            .unwrap_or_else(|| {
                dirs_next::home_dir()
                    .expect("Unable to find home directory")
                    .join(".cache/starship")
            });

        fs::create_dir_all(&log_dir)
            .unwrap_or_else(|err| panic!("Unable to create log dir {:?}: {:?}!", log_dir, err));
        let session_log_file = log_dir.join(format!(
            "session_{}.log",
            env::var("STARSHIP_SESSION_KEY").unwrap_or_default()
        ));

        Self {
            log_file_content: fs::read_to_string(&session_log_file)
                .unwrap_or_default()
                .lines()
                .map(|line| line.to_string())
                .collect(),
            log_file: OnceCell::new(),
            log_file_path: session_log_file,
            log_level: env::var("STARSHIP_LOG")
                .map(|level| match level.to_lowercase().as_str() {
                    "trace" => Level::Trace,
                    "debug" => Level::Debug,
                    "info" => Level::Info,
                    "warn" => Level::Warn,
                    "error" => Level::Error,
                    _ => Level::Warn,
                })
                .unwrap_or_else(|_| Level::Warn),
        }
    }
}

impl StarshipLogger {
    /// Override the minimum log level
    pub fn set_log_level(&mut self, level: log::Level) {
        self.log_level = level;
    }

    /// Override the log level path
    /// This won't change anything if a log file was already opened
    pub fn set_log_file_path(&mut self, path: PathBuf) {
        self.log_file_path = path;
    }
}

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

    fn log(&self, record: &Record) {
        let to_print = format!(
            "[{}] - ({}): {}",
            record.level(),
            record.module_path().unwrap_or_default(),
            record.args()
        );

        if record.metadata().level() <= Level::Warn {
            self.log_file
                .get_or_try_init(|| {
                    let m = Mutex::new(
                        OpenOptions::new()
                            .create(true)
                            .append(true)
                            .open(&self.log_file_path)?,
                    );
                    Ok(m)
                })
                .unwrap_or_else(|err: std::io::Error| {
                    panic!(
                        "Unable to open session log file {:?}: {:?}!",
                        self.log_file_path, err
                    )
                })
                .lock()
                .map(|mut file| writeln!(file, "{}", to_print))
                .expect("Log file writer mutex was poisoned!")
                .expect("Unable to write to the log file!");
        }

        if self.enabled(record.metadata()) && !self.log_file_content.contains(to_print.as_str()) {
            eprintln!(
                "[{}] - ({}): {}",
                match record.level() {
                    Level::Trace => Color::Blue.dimmed().paint(format!("{}", record.level())),
                    Level::Debug => Color::Cyan.paint(format!("{}", record.level())),
                    Level::Info => Color::White.paint(format!("{}", record.level())),
                    Level::Warn => Color::Yellow.paint(format!("{}", record.level())),
                    Level::Error => Color::Red.paint(format!("{}", record.level())),
                },
                record.module_path().unwrap_or_default(),
                record.args()
            );
        }
    }

    fn flush(&self) {
        if let Some(m) = self.log_file.get() {
            m.lock()
                .map(|mut writer| writer.flush())
                .expect("Log file writer mutex was poisoned!")
                .expect("Unable to flush the log file!");
        }
    }
}

pub fn init() {
    log::set_boxed_logger(Box::new(StarshipLogger::default())).unwrap();
    log::set_max_level(LevelFilter::Trace);
}