use alloc::string::String; use alloc::vec::Vec; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex as BlockingMutex; use embassy_sync::mutex::Mutex; use log::{LevelFilter, Log, Metadata, Record}; pub struct InterceptorLogger { // Async mutex for start/stop capture from async context async_capture: Mutex, // Blocking mutex for the actual data to be used in sync log() sync_capture: BlockingMutex>>>, } impl InterceptorLogger { pub const fn new() -> Self { Self { async_capture: Mutex::new(()), sync_capture: BlockingMutex::new(core::cell::RefCell::new(None)), } } pub async fn start_capture(&self) { let _guard = self.async_capture.lock().await; self.sync_capture.lock(|capture| { *capture.borrow_mut() = Some(Vec::new()); }); } pub async fn stop_capture(&self) -> Option> { let _guard = self.async_capture.lock().await; self.sync_capture .lock(|capture| capture.borrow_mut().take()) } pub fn init(&'static self) { log::set_logger(self) .map(|()| log::set_max_level(LevelFilter::Info)) .unwrap(); } } impl Log for InterceptorLogger { fn enabled(&self, metadata: &Metadata) -> bool { metadata.level() <= log::Level::Info } fn log(&self, record: &Record) { if self.enabled(record.metadata()) { let message = alloc::format!("{}", record.args()); // Print to serial using esp_println esp_println::println!("{}: {}", record.level(), message); // Capture if active self.sync_capture.lock(|capture| { if let Some(ref mut buffer) = *capture.borrow_mut() { buffer.push(alloc::format!("{}: {}", record.level(), message)); } }); } } fn flush(&self) {} }