use std::cell::RefCell; use ringbuf::{RingBuffer, Producer, Consumer}; use lazy_static::lazy_static; use std::sync::{Arc, Mutex}; lazy_static! { static ref LOG_RECV: Arc> = { Arc::new(Mutex::new(LogReceiver::new())) }; } thread_local! { pub static LOG: RefCell> = RefCell::new(None); } pub fn retrieve_log_messages(msgs: &mut Vec) { if let Ok(mut lr) = LOG_RECV.lock() { lr.retrieve_messages(msgs); } } pub fn init_thread_logger() { if let Ok(mut lr) = LOG_RECV.lock() { lr.spawn_global_logger(); } } pub fn log)>(f: F) { use std::borrow::BorrowMut; LOG.with(|l| { let mut lh = l.borrow_mut(); if let Some(lh) = (*(*lh.borrow_mut())).as_mut() { lh.log(f); } }); } const MAX_LOG_BUFFER : usize = 4096; pub struct LogReceiver { consumers: Vec>, } impl LogReceiver { pub fn new() -> Self { Self { consumers: vec![], } } pub fn retrieve_messages(&mut self, out: &mut Vec) { for consumer in self.consumers.iter_mut() { let mut buf = [0; 1024]; let mut oi = 0; while let Some(byte) = consumer.pop() { if oi >= buf.len() || byte == 0xFF { out.push( std::str::from_utf8(&buf[0..oi]) .unwrap() .to_string()); oi = 0; } else { buf[oi] = byte; oi += 1; } } } } pub fn spawn_logger(&mut self) -> Log { let rb = RingBuffer::new(MAX_LOG_BUFFER); let (producer, con) = rb.split(); self.consumers.push(con); Log { producer, buf: [0; 512], } } pub fn spawn_global_logger(&mut self) { let hdl = self.spawn_logger(); LOG.with(move |f| { *f.borrow_mut() = Some(hdl); }); } } pub struct Log { producer: Producer, buf: [u8; 512], } impl Log { pub fn log_buf(&mut self, data: &[u8]) { self.producer.push_slice(data); let _ = self.producer.push(0xFF); } pub fn log)>(&mut self, f: F) { self.buf.fill(0xFF); let len = { let mut bw = std::io::BufWriter::new(&mut self.buf[..]); f(&mut bw); bw.buffer().len() }; if len < (self.buf.len() - 1) { // include one 0xFF! self.producer.push_slice(&self.buf[0..len + 1]); } else { self.producer.push_slice(&self.buf[0..len]); } } } #[cfg(test)] mod tests { use super::*; #[test] fn check_threaded_logger() { std::thread::spawn(|| { use std::io::Write; init_thread_logger(); log(|w| write!(w, "Test Log{}!", 1).unwrap()); log(|w| write!(w, "Test Log{}!", 2).unwrap()); }); let mut msgs = vec![]; for _ in 0..100 { std::thread::sleep( std::time::Duration::from_millis(100)); retrieve_log_messages(&mut msgs); if msgs.len() > 1 { assert_eq!(msgs[0], "Test Log1!"); assert_eq!(msgs[1], "Test Log2!"); break; } }; } }