1use env_logger::{
2 fmt::{Color, Formatter},
3 Builder as LogBuilder,
4};
5use log::LevelFilter;
6use std::io::{IsTerminal, Write};
7
8#[derive(Debug, PartialEq, Clone)]
9pub struct Config {
10 pub mode: Option<String>,
11 pub color: bool,
12 pub file: Option<String>,
13}
14
15impl Default for Config {
16 fn default() -> Self {
17 Config { mode: None, color: !cfg!(windows), file: None }
18 }
19}
20
21pub static LOG_ENABLED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(true);
22
23pub fn setup_log(config: &Config) {
24 let mut levels = String::new();
25 let mut builder = LogBuilder::new();
26
27 builder.filter(None, LevelFilter::Info);
28
29 if let Ok(lvl) = std::env::var("RUST_LOG") {
30 levels.push_str(&lvl);
31 levels.push(',');
32 builder.parse_filters(&lvl);
33 }
34
35 if let Some(ref s) = config.mode {
36 levels.push_str(s);
37 builder.parse_filters(s);
38 }
39
40 let isatty = std::io::stderr().is_terminal();
41 let enable_color = config.color && isatty;
42
43 let format = move |buf: &mut Formatter, record: &log::Record| {
44 let mut bold = buf.style();
45 bold.set_bold(true);
46 let mut blue_bold = buf.style();
47 blue_bold.set_color(Color::Blue).set_bold(true);
48
49 let dt = chrono::Local::now();
50 let timestamp = dt.format("%Y-%m-%d %H:%M:%S");
51
52 let with_color = if enable_color && log::max_level() <= LevelFilter::Info {
53 format!("{} {}", bold.value(timestamp), record.args(),)
54 } else {
55 let name = std::thread::current()
56 .name()
57 .map_or_else(Default::default, |x| format!("{}", blue_bold.value(x)));
58 format!(
59 "{} {} {} {} {}",
60 bold.value(timestamp),
61 name,
62 record.level(),
63 record.target(),
64 record.args()
65 )
66 };
67
68 if !isatty && record.level() <= log::Level::Info && std::io::stdout().is_terminal() {
69 println!("{with_color}");
71 }
72
73 if LOG_ENABLED.load(std::sync::atomic::Ordering::Relaxed) {
74 writeln!(buf, "{with_color}")?;
75 }
76 Ok(())
77 };
78
79 builder.format(format);
80 let _ = builder.try_init();
81}