File size: 2,259 Bytes
f14b4e9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
use indicatif::MultiProgress;
use indicatif_log_bridge::LogWrapper;
use log::{Level, Log, Metadata, Record};
use std::sync::OnceLock;

use super::ui;

/// Tracks indicatif progress bars so they can be stalled when outputting logs.
static MULTI_PROGRESS: OnceLock<MultiProgress> = OnceLock::new();

/// Gets or creates a global indicatif progress bar tracker.
pub fn get_multi_progress() -> &'static MultiProgress {
    MULTI_PROGRESS.get_or_init(MultiProgress::new)
}

/// A logger implementation that outputs library logs to console UI messages.
struct UiLogger {
    verbose: bool,
}

impl Log for UiLogger {
    /// Configures logger to take logs from firm libraries.
    fn enabled(&self, metadata: &Metadata) -> bool {
        // Determine if the log originates from a firm crate
        let target = metadata.target();
        let is_firm_crate = target.contains("firm");

        // In verbose mode, set debug level for firm crates and warn level for others
        if self.verbose && is_firm_crate {
            metadata.level() <= Level::Debug
        } else {
            metadata.level() <= Level::Warn
        }
    }

    /// Pipes library logs to the appropriate UI message.
    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            match record.level() {
                Level::Error => ui::error(&record.args().to_string()),
                Level::Warn => ui::warning(&record.args().to_string()),
                Level::Info => ui::info(&record.args().to_string()),
                Level::Debug => ui::debug(&record.args().to_string()),
                Level::Trace => ui::debug(&record.args().to_string()),
            }
        }
    }

    fn flush(&self) {}
}

/// Initializes logging for the CLI.
pub fn initialize(verbose: bool) -> Result<(), log::SetLoggerError> {
    let ui_logger = UiLogger { verbose };

    // Wrap logger, allowing indicatif progress bars to be suspended when we output logs
    let wrapped_logger = LogWrapper::new(get_multi_progress().clone(), Box::new(ui_logger));
    log::set_boxed_logger(Box::new(wrapped_logger))?;

    if verbose {
        log::set_max_level(log::LevelFilter::Debug);
    } else {
        log::set_max_level(log::LevelFilter::Warn);
    }

    Ok(())
}