1
2use std::{cmp, process};
3use std::io::Write;
4use std::path::Path;
5
6struct 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 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 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 %:z");
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 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}