rtapi_logger/
lib.rs

1//! `rtapi-logger` is a logging driver for the [`log`](https://docs.rs/log) ecosystem.
2//!
3//! Other loggers which don't use the RTAPI logging machinery provided by LinuxCNC are relatively
4//! slow and can unnecessarily block realtime tasks. `rtapi-logger` hooks into LinuxCNC's logging
5//! machinery to prevent these problems, whilst also allowing the use of the convenient macros
6//! provided by [`log`].
7//!
8//! Please note this crate is still somewhat experimental. For example, currently all messages are
9//! logged at the `ERR` level provided by LinuxCNC.
10
11use linuxcnc_hal_sys::{rtapi_get_msg_level, rtapi_print_msg};
12use log::{Level, Metadata, Record};
13use std::ffi::CString;
14
15use log::{LevelFilter, SetLoggerError};
16
17/// Log level.
18///
19/// Defined in the LinuxCNC source as `msg_level_t`.
20///
21/// Note that this currently does nothing; all messages are logged at `ERR` level.
22#[derive(Debug, Copy, Clone)]
23enum RtapiLogLevel {
24    #[allow(unused)]
25    None = 0,
26    Err = 1,
27    Warn = 2,
28    Info = 3,
29    Dbg = 4,
30    All = 5,
31}
32
33impl From<Level> for RtapiLogLevel {
34    fn from(other: Level) -> Self {
35        match other {
36            Level::Error => Self::Err,
37            Level::Warn => Self::Warn,
38            Level::Info => Self::Info,
39            Level::Debug => Self::Dbg,
40            Level::Trace => Self::All,
41        }
42    }
43}
44
45static LOGGER: RtapiLogger = RtapiLogger;
46
47pub fn init() -> Result<(), SetLoggerError> {
48    let _rtapi_level = unsafe { rtapi_get_msg_level() };
49
50    // log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Info))
51    // Log everything by default
52    log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Trace))
53}
54
55pub struct RtapiLogger;
56
57impl log::Log for RtapiLogger {
58    // FIXME: This should not just return true - performance will take a hit. LinuxCNC's logging
59    // system does the filtering, so that works at least.
60    fn enabled(&self, _metadata: &Metadata) -> bool {
61        // metadata.level() <= Level::Info
62        true
63    }
64
65    fn log(&self, record: &Record) {
66        if self.enabled(record.metadata()) {
67            let out = format!("{}\n", record.args());
68
69            // FIXME: LinuxCNC seems to always use `Err` level regardless of DEBUG config value.
70            // let level: RtapiLogLevel = record.level().into();
71            let level = RtapiLogLevel::Err;
72
73            if let Ok(f) = CString::new(out) {
74                unsafe { rtapi_print_msg(level as u32, f.as_ptr()) };
75            } else {
76                let fail = CString::new("failed to build log message string").unwrap();
77
78                unsafe { rtapi_print_msg(level as u32, fail.as_ptr()) }
79            }
80        }
81    }
82
83    fn flush(&self) {}
84}