Skip to main content

bark_cli/
log.rs

1
2use std::{cmp, process};
3use std::io::Write;
4use std::path::Path;
5
6/// Simple logger that splits into two logger
7struct SplitLogger {
8	log1: env_logger::Logger,
9	log2: env_logger::Logger,
10}
11
12impl SplitLogger {
13	fn init(log1: env_logger::Logger, log2: env_logger::Logger) {
14		let max_level = cmp::max(log1.filter(), log2.filter());
15		log::set_boxed_logger(Box::new(SplitLogger {
16			log1: log1,
17			log2: log2,
18		})).expect("error initializing split logger");
19		log::set_max_level(max_level);
20	}
21}
22
23impl log::Log for SplitLogger {
24	fn enabled(&self, m: &log::Metadata) -> bool {
25	    self.log1.enabled(m) || self.log2.enabled(m)
26	}
27
28	fn flush(&self) {
29	    self.log1.flush();
30		self.log2.flush();
31	}
32
33	fn log(&self, rec: &log::Record) {
34		self.log1.log(rec);
35		self.log2.log(rec);
36	}
37}
38
39pub fn init_logging(verbose: bool, quiet: bool, datadir: &Path) {
40	if verbose && quiet {
41		println!("Can't set both --verbose and --quiet");
42		process::exit(1);
43	}
44
45	let env = env_logger::Env::new().filter("BARK_LOG");
46
47	// Builder has no clone and we don't want to repeat this
48	fn base() -> env_logger::Builder {
49		let mut builder = env_logger::Builder::new();
50		builder
51			.filter_module("rusqlite", log::LevelFilter::Warn)
52			.filter_module("rustls", log::LevelFilter::Warn)
53			.filter_module("reqwest", log::LevelFilter::Warn)
54			.filter_module("ureq", log::LevelFilter::Warn)
55			.filter_module("ureq_proto", log::LevelFilter::Warn);
56		builder
57	}
58
59	let terminal = if !quiet {
60		let mut logger = base();
61
62		// We first set the default and then let the env_logger
63		// env overwrite it.
64		logger.filter_level(if verbose {
65			log::LevelFilter::Trace
66		} else {
67			log::LevelFilter::Info
68		});
69
70		logger.parse_env(env)
71			.format(move |out, rec| {
72				let now = chrono::Local::now();
73				let ts = now.format("%Y-%m-%d %H:%M:%S.%3f");
74				let lvl = rec.level();
75				let msg = rec.args();
76				if verbose {
77					let module = rec.module_path().expect("no module");
78					if module.starts_with("bark") {
79						let file = rec.file().expect("our macro provides file");
80						let file = file.split("bark/src/").last().unwrap();
81						let line = rec.line().expect("our macro provides line");
82						writeln!(out, "[{ts} {lvl: >5} {module} {file}:{line}] {msg}")
83					} else {
84						writeln!(out, "[{ts} {lvl: >5} {module}] {msg}")
85					}
86				} else {
87					writeln!(out, "[{ts} {lvl: >5}] {msg}")
88				}
89			})
90			.target(env_logger::Target::Stderr);
91		Some(logger)
92	} else {
93		None
94	};
95
96	let logfile = if datadir.exists() {
97		let path = datadir.join("debug.log");
98		match std::fs::File::options().create(true).append(true).open(path) {
99			Ok(mut file) => {
100				// try write a newline into the file to separate commands
101				let _ = file.write_all("\n\n".as_bytes());
102				let mut logger = base();
103				logger
104					.filter_level(log::LevelFilter::Trace)
105					.format_timestamp_millis()
106					.format_module_path(true)
107					.format_file(true)
108					.format_line_number(true)
109					.target(env_logger::Target::Pipe(Box::new(file)));
110				Some(logger)
111			},
112			Err(e) => {
113				eprintln!("Failed to open debug.log file: {:#}", e);
114				None
115			},
116		}
117	} else {
118		None
119	};
120
121	match (terminal, logfile) {
122		(Some(mut l1), Some(mut l2)) => SplitLogger::init(l1.build(), l2.build()),
123		(Some(mut l), None) => l.init(),
124		(None, Some(mut l)) => l.init(),
125		(None, None) => {},
126	}
127}