1use std::{
4 env,
5 io::{stdout, Write},
6};
7
8use colored::{control::set_override, Colorize};
9use log::{Level, LevelFilter, Log, Metadata, Record};
10
11#[derive(Default)]
12struct SimpleLogger;
13
14impl SimpleLogger {
15 fn level_color(level: &Level) -> String {
16 let name = format!("{:>5}", level.as_str().to_uppercase());
17 match level {
18 Level::Error => name.red().bold().to_string(),
19 Level::Warn => name.yellow().bold().to_string(),
20 Level::Info => name.green().bold().to_string(),
21 Level::Debug => name.blue().bold().to_string(),
22 Level::Trace => name.magenta().bold().to_string(),
23 }
24 }
25}
26
27impl Log for SimpleLogger {
28 fn enabled(&self, metadata: &Metadata) -> bool {
29 metadata.level() <= log::max_level()
30 }
31
32 fn log(&self, record: &Record) {
33 let mut stdout = stdout().lock();
34 if record.target() == "CI_LOG_GROUPING" {
35 writeln!(stdout, "{}", record.args()).expect("Failed to write log command to stdout");
37 stdout
38 .flush()
39 .expect("Failed to flush log command to stdout");
40 } else if self.enabled(record.metadata()) {
41 let module = record.module_path();
42 if module.is_none_or(|v| v.starts_with("cpp_linter")) {
43 writeln!(
44 stdout,
45 "[{}]: {}",
46 Self::level_color(&record.level()),
47 record.args()
48 )
49 .expect("Failed to write log message to stdout");
50 } else {
51 writeln!(
52 stdout,
53 "[{}]{{{}:{}}}: {}",
54 Self::level_color(&record.level()),
55 module.unwrap(), record.line().unwrap_or_default(),
57 record.args()
58 )
59 .expect("Failed to write detailed log message to stdout");
60 }
61 stdout
62 .flush()
63 .expect("Failed to flush log message to stdout");
64 }
65 }
66
67 fn flush(&self) {}
68}
69
70pub fn try_init() {
76 let logger: SimpleLogger = SimpleLogger;
77 if matches!(
78 env::var("CPP_LINTER_COLOR").as_deref(),
79 Ok("on" | "1" | "true")
80 ) {
81 set_override(true);
82 }
83 if let Err(e) =
84 log::set_boxed_logger(Box::new(logger)).map(|()| log::set_max_level(LevelFilter::Info))
85 {
86 log::debug!("{e:?}");
89 }
90}
91
92#[cfg(test)]
93mod test {
94 use std::env;
95
96 use super::{try_init, SimpleLogger};
97
98 #[test]
99 fn trace_log() {
100 env::set_var("CPP_LINTER_COLOR", "true");
101 try_init();
102 assert!(SimpleLogger::level_color(&log::Level::Trace).contains("TRACE"));
103 log::set_max_level(log::LevelFilter::Trace);
104 log::trace!("A dummy log statement for code coverage");
105 }
106}