cli/
logger.rs

1//! # log
2//!
3//! Initializes the global logger.
4//!
5
6#[cfg(test)]
7#[path = "logger_test.rs"]
8mod logger_test;
9
10use crate::recursion_level;
11use crate::types::FlowInfo;
12use colored::{ColoredString, Colorize};
13use log::{Level, LevelFilter};
14use std::io::stdout;
15use std::process::exit;
16
17#[derive(Debug, PartialEq)]
18/// The log levels
19pub(crate) enum LogLevel {
20    VERBOSE,
21    INFO,
22    ERROR,
23    OFF,
24}
25
26/// The logger options used to initialize the logger
27pub struct LoggerOptions {
28    /// Name to prefix each log message
29    pub name: String,
30    /// The logger level name (verbose, info, error, off)
31    pub level: String,
32    /// True to printout colorful output
33    pub color: bool,
34}
35
36pub(crate) fn get_level(level_name: &str) -> LogLevel {
37    match level_name {
38        "verbose" => LogLevel::VERBOSE,
39        "error" => LogLevel::ERROR,
40        "off" => LogLevel::OFF,
41        _ => LogLevel::INFO,
42    }
43}
44
45/// Returns the current logger level name
46pub(crate) fn get_log_level() -> String {
47    if envmnt::is_equal("CARGO_MAKE_LOG_LEVEL", "off") {
48        "off"
49    } else if log_enabled!(Level::Debug) {
50        "verbose"
51    } else if log_enabled!(Level::Info) {
52        "info"
53    } else {
54        "error"
55    }
56    .to_string()
57}
58
59fn get_name_for_filter(filter: &LevelFilter) -> String {
60    let level = match filter {
61        LevelFilter::Off => return "off".to_string(),
62        LevelFilter::Debug => Level::Debug,
63        LevelFilter::Warn => Level::Warn,
64        LevelFilter::Error => Level::Error,
65        _ => Level::Info,
66    };
67
68    get_name_for_level(&level)
69}
70
71fn get_name_for_level(level: &Level) -> String {
72    match level {
73        Level::Debug => "verbose".to_string(),
74        Level::Warn => "warn".to_string(),
75        Level::Error => "error".to_string(),
76        _ => "info".to_string(),
77    }
78}
79
80fn get_formatted_name(name: &str, use_color: bool) -> ColoredString {
81    if use_color {
82        name.bold()
83    } else {
84        name.normal()
85    }
86}
87
88fn get_formatted_log_level(level: &Level, use_color: bool) -> ColoredString {
89    let mut level_name = get_name_for_level(&level);
90    level_name = level_name.to_uppercase();
91
92    if use_color {
93        let fmt_value = match level {
94            Level::Debug => level_name.cyan(),
95            Level::Info => level_name.green(),
96            Level::Warn => level_name.yellow(),
97            Level::Error => level_name.red(),
98            _ => level_name.normal(),
99        };
100
101        fmt_value.bold()
102    } else {
103        level_name.normal()
104    }
105}
106
107pub(crate) fn should_reduce_output(flow_info: &FlowInfo) -> bool {
108    match flow_info.config.config.reduce_output {
109        Some(value) => value,
110        None => !envmnt::is("CARGO_MAKE_CI"),
111    }
112}
113
114/// Initializes the global logger.
115///
116/// # Arguments
117///
118/// * `level_name` - The log level name ('verbose', 'info', 'error', 'off')
119pub(crate) fn init(options: &LoggerOptions) {
120    let level_name = &options.level;
121    let color = options.color;
122
123    let level = get_level(level_name);
124
125    let log_level = match level {
126        LogLevel::VERBOSE => LevelFilter::Debug,
127        LogLevel::INFO => LevelFilter::Info,
128        LogLevel::ERROR => LevelFilter::Error,
129        LogLevel::OFF => LevelFilter::Off,
130    };
131    let level_name_value = get_name_for_filter(&log_level);
132
133    envmnt::set("CARGO_MAKE_LOG_LEVEL", &level_name_value);
134    envmnt::set_bool("CARGO_MAKE_DISABLE_COLOR", !color);
135
136    let recursion_lvl = recursion_level::get();
137    let recursion_level_log = if recursion_lvl == 0 {
138        "".to_string()
139    } else {
140        format!("[{}]", recursion_lvl)
141    };
142
143    let name_fmt = get_formatted_name(&options.name, color);
144
145    let result = fern::Dispatch::new()
146        .format(move |out, message, record| {
147            let record_level = record.level();
148
149            if cfg!(test) {
150                if record_level == LevelFilter::Error {
151                    panic!("test error flow: {}", message);
152                }
153            }
154
155            let record_level_fmt = get_formatted_log_level(&record_level, color);
156
157            out.finish(format_args!(
158                "[{}]{} {} - {}",
159                &name_fmt, &recursion_level_log, &record_level_fmt, &message
160            ));
161
162            if record_level == Level::Error {
163                warn!("Build Failed.");
164
165                exit(1);
166            }
167        })
168        .level(log_level)
169        .chain(stdout())
170        .apply();
171
172    if result.is_err() {
173        println!("Unable to setup logger.");
174    }
175}