xous_api_log_server/
lib.rs

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