xous_api_log/
lib.rs

1#![cfg_attr(any(target_os = "none", feature = "nostd"), no_std)]
2use core::fmt::Write;
3use core::sync::atomic::{AtomicU32, Ordering};
4
5use num_traits::ToPrimitive;
6
7pub mod api;
8mod cursor;
9
10#[derive(Debug)]
11pub enum LogError {
12    LoggerExists,
13    NoConnection,
14}
15
16struct XousLogger;
17static XOUS_LOGGER: XousLogger = XousLogger {};
18static XOUS_LOGGER_CONNECTION: AtomicU32 = AtomicU32::new(0);
19
20impl XousLogger {
21    fn log_impl(&self, record: &log::Record) {
22        let mut log_record = api::LogRecord::default();
23        assert_eq!(core::mem::size_of::<api::LogRecord>(), 4096);
24
25        // A "line" of 0 is the same as "None" for our purposes here.
26        log_record.line = core::num::NonZeroU32::new(record.line().unwrap_or_default());
27
28        log_record.level = record.level() as u32;
29
30        let file = record.file().unwrap_or_default().as_bytes();
31        log_record.file_length = file.len().min(file.len()) as u32;
32        for (dest, src) in log_record.file.iter_mut().zip(file) {
33            *dest = *src;
34        }
35
36        let module = record.module_path().unwrap_or_default().as_bytes();
37        log_record.module_length = module.len().min(module.len()) as u32;
38        for (dest, src) in log_record.module.iter_mut().zip(module) {
39            *dest = *src;
40        }
41
42        // Serialize the text to our record buffer
43        let mut wrapper = cursor::BufferWrapper::new(&mut log_record.args);
44        write!(wrapper, "{}", record.args()).ok(); // truncate if error
45        log_record.args_length = wrapper.len().min(log_record.args.len()) as u32;
46
47        let buf = unsafe {
48            xous::MemoryRange::new(
49                &log_record as *const api::LogRecord as usize,
50                core::mem::size_of::<api::LogRecord>(),
51            )
52            .unwrap()
53        };
54
55        xous::send_message(
56            XOUS_LOGGER_CONNECTION.load(Ordering::Relaxed),
57            xous::Message::new_lend(crate::api::Opcode::LogRecord.to_usize().unwrap(), buf, None, None),
58        )
59        .unwrap();
60    }
61
62    fn resume(&self) {
63        xous::send_message(
64            XOUS_LOGGER_CONNECTION.load(Ordering::Relaxed),
65            xous::Message::new_scalar(2000, 0, 0, 0, 0), /* logger is one of the few servers that uses
66                                                          * special, non-encoded message IDs. */
67        )
68        .expect("couldn't send resume message to the logger implementation");
69    }
70}
71
72impl log::Log for XousLogger {
73    fn enabled(&self, _metadata: &log::Metadata) -> bool { true }
74
75    fn log(&self, record: &log::Record) { XOUS_LOGGER.log_impl(record); }
76
77    fn flush(&self) {}
78}
79
80pub fn init() -> Result<(), LogError> {
81    XOUS_LOGGER_CONNECTION.store(
82        xous::try_connect(xous::SID::from_bytes(b"xous-log-server ").unwrap())
83            .or(Err(LogError::NoConnection))?,
84        Ordering::Relaxed,
85    );
86    log::set_logger(&XOUS_LOGGER).map_err(|_| LogError::LoggerExists)?;
87    log::set_max_level(log::LevelFilter::Info);
88    Ok(())
89}
90
91pub fn init_wait() -> Result<(), ()> {
92    let sid = xous::SID::from_bytes(b"xous-log-server ").unwrap();
93    let cid = xous::connect(sid).or(Err(()))?;
94    XOUS_LOGGER_CONNECTION.store(cid, Ordering::Relaxed);
95    log::set_logger(&XOUS_LOGGER).or(Err(()))?;
96    log::set_max_level(log::LevelFilter::Info);
97    Ok(())
98}
99
100pub fn resume() { XOUS_LOGGER.resume(); }