1#![doc(html_root_url = "https://docs.rs/capture-logger/0.1.1")]
2use std::cell::RefCell;
38use std::collections::VecDeque;
39use std::sync::Once;
40
41pub use log::Level;
42
43static INITIALIZED: Once = Once::new();
44
45#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct Record {
48 level: Level,
49 message: String,
50}
51
52impl Record {
53 pub fn level(&self) -> Level {
55 self.level
56 }
57
58 pub fn message(&self) -> &str {
60 &self.message
61 }
62}
63
64thread_local! {
65 static CAPTURED: RefCell<Option<VecDeque<Record>>> = RefCell::new(None);
66}
67
68static LOGGER: Logger = Logger;
69
70struct Logger;
71
72impl log::Log for Logger {
73 fn enabled(&self, _: &log::Metadata) -> bool {
74 true
75 }
76
77 fn log(&self, record: &log::Record) {
78 CAPTURED.with(|cap| {
79 if let Some(queue) = &mut *cap.borrow_mut() {
80 queue.push_back(Record {
81 level: record.level(),
82 message: format!("{}", record.args()),
83 });
84 }
85 });
86 }
87
88 fn flush(&self) {}
89}
90
91pub fn begin_capture() {
97 INITIALIZED.call_once(|| {
98 log::set_logger(&LOGGER).unwrap();
99 log::set_max_level(log::LevelFilter::Trace);
100 });
101
102 CAPTURED.with(|cap| {
103 let queue = &mut *cap.borrow_mut();
104 if queue.is_none() {
105 *queue = Some(VecDeque::new());
106 } else {
107 panic!("already captured.")
108 }
109 });
110}
111
112pub fn end_capture() {
114 CAPTURED.with(|cap| {
115 *cap.borrow_mut() = None;
116 });
117}
118
119pub fn pop_captured() -> Option<Record> {
125 CAPTURED.with(|cap| {
126 if let Some(queue) = &mut *cap.borrow_mut() {
127 queue.pop_front()
128 } else {
129 panic!("not captured.");
130 }
131 })
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137 use log::Log;
138
139 #[test]
140 fn test_logger() {
141 LOGGER.flush(); }
143}