data_beans/aux/
logging.rs1use std::sync::Mutex;
15
16pub use legume_numeric::matrix::progress::{new_progress_bar, new_spinner, MULTI_PROGRESS};
17
18pub fn init_logger(verbose: bool) {
23 let default_filter = if verbose {
24 legume_numeric::matrix::common_io::VERBOSE_LOG_FILTER
25 } else {
26 legume_numeric::matrix::common_io::QUIET_LOG_FILTER
27 };
28 let logger =
29 env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(default_filter))
30 .build();
31 let max_level = logger.filter();
32 let wrapped = indicatif_log_bridge::LogWrapper::new(MULTI_PROGRESS.clone(), logger);
33 let _ =
34 log::set_boxed_logger(Box::new(Holding(wrapped))).map(|()| log::set_max_level(max_level));
35}
36
37static HELD: Mutex<Option<Vec<(log::Level, String, String)>>> = Mutex::new(None);
40
41struct Holding<L>(L);
44
45impl<L: log::Log> log::Log for Holding<L> {
46 fn enabled(&self, metadata: &log::Metadata) -> bool {
47 self.0.enabled(metadata)
48 }
49
50 fn log(&self, record: &log::Record) {
51 if let Some(held) = HELD.lock().unwrap_or_else(|e| e.into_inner()).as_mut() {
52 if self.0.enabled(record.metadata()) {
53 let entry = (
54 record.level(),
55 record.target().into(),
56 record.args().to_string(),
57 );
58 held.push(entry);
59 }
60 return;
61 }
62 self.0.log(record);
63 }
64
65 fn flush(&self) {
66 self.0.flush();
67 }
68}
69
70pub fn hold_logs(on: bool) {
75 let held = {
76 let mut slot = HELD.lock().unwrap_or_else(|e| e.into_inner());
77 if on {
78 slot.get_or_insert_with(Vec::new);
79 return;
80 }
81 slot.take()
82 };
83 for (level, target, message) in held.into_iter().flatten() {
84 log::logger().log(
85 &log::Record::builder()
86 .level(level)
87 .target(&target)
88 .args(format_args!("{message}"))
89 .build(),
90 );
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97 use log::Log;
98 use std::sync::atomic::{AtomicUsize, Ordering};
99
100 struct Counting(AtomicUsize);
101
102 impl Log for Counting {
103 fn enabled(&self, _: &log::Metadata) -> bool {
104 true
105 }
106 fn log(&self, _: &log::Record) {
107 self.0.fetch_add(1, Ordering::Relaxed);
108 }
109 fn flush(&self) {}
110 }
111
112 #[test]
113 fn held_records_wait_for_release() {
114 let logger = Holding(Counting(AtomicUsize::new(0)));
115 let record = |msg| {
116 logger.log(
117 &log::Record::builder()
118 .level(log::Level::Warn)
119 .args(format_args!("{msg}"))
120 .build(),
121 )
122 };
123 hold_logs(true);
124 record("held");
125 assert_eq!(logger.0 .0.load(Ordering::Relaxed), 0, "nothing written");
126 let held = HELD.lock().unwrap().as_ref().map(|h| h.len());
127 assert_eq!(held, Some(1));
128 hold_logs(false);
129 assert!(HELD.lock().unwrap().is_none(), "released");
130 record("through");
131 assert_eq!(logger.0 .0.load(Ordering::Relaxed), 1);
132 }
133}