leodos_libcfs/log.rs
1//! Safe, ergonomic logging facilities for cFS.
2//!
3//! ## System log and console
4//!
5//! The [`log!`] and [`printf!`] macros write to the cFE System Log
6//! and the OSAL console, respectively.
7//!
8//! ## Event Services (EVS)
9//!
10//! The [`info!`], [`warn!`], and [`err!`] macros send EVS events
11//! with `println!`-like formatting. The event ID is derived from
12//! the source line number automatically.
13//!
14//! ```rust,ignore
15//! info!("system nominal")?;
16//! warn!("temperature high: {} C", temp)?;
17//! err!("{} failed", subsystem)?;
18//! ```
19
20use crate::error::{CfsError, OsalError};
21use crate::error::Result;
22use crate::ffi;
23use crate::status::check;
24use heapless::CString;
25
26/// The maximum size of a single `OS_printf` message, from OSAL configuration.
27pub const MAX_PRINTF_MSG_SIZE: usize = ffi::OS_BUFFER_SIZE as usize;
28
29/// Writes a message string to the OSAL console (`OS_printf`).
30///
31/// This is a low-level wrapper around the C `OS_printf` function. The `printf!`
32/// macro is generally more convenient to use. This function does not return an
33/// error and is considered a "best-effort" logging mechanism.
34///
35/// # Arguments
36/// * `message`: The string to write. It will be truncated if its byte length
37/// exceeds `MAX_PRINTF_MSG_SIZE`.
38pub fn printf(message: &str) {
39 let mut c_message = CString::<MAX_PRINTF_MSG_SIZE>::new();
40
41 // extend_from_bytes will truncate if the message is too long, which is
42 // acceptable behavior for this best-effort printf wrapper.
43 let _ = c_message.extend_from_bytes(message.as_bytes());
44
45 unsafe {
46 // We call the variadic C function by passing the fully formatted
47 // Rust string as a single argument to a simple "%s" format specifier.
48 ffi::OS_printf("%s\0".as_ptr() as *const libc::c_char, c_message.as_ptr());
49 }
50}
51
52/// Enables output from the `printf!` macro and the underlying `OS_printf` function.
53pub fn printf_enable() {
54 unsafe {
55 ffi::OS_printf_enable();
56 }
57}
58
59/// Disables output from the `printf!` macro and the underlying `OS_printf` function.
60pub fn printf_disable() {
61 unsafe {
62 ffi::OS_printf_disable();
63 }
64}
65
66/// A macro to write a formatted string to the cFE System Log.
67///
68/// This macro provides a `println!`-like interface for `CFE_ES_WriteToSysLog`.
69/// It handles the necessary string formatting and C string conversion.
70///
71/// # Usage
72///
73/// ```rust,ignore
74/// use libcfs::log::syslog;
75/// use libcfs::error::Result;
76///
77/// fn my_function() -> Result<()> {
78/// // Simple literal message
79/// log!("Starting task...")?;
80///
81/// let event_count = 10;
82/// let status = 0;
83///
84/// // Formatted message
85/// log!("Processed {} events with status {}", event_count, status)?;
86///
87/// Ok(())
88/// }
89/// ```
90///
91/// # Returns
92///
93/// This macro evaluates to a `libcfs::error::Result<()>`. It will return an
94/// error if the message cannot be formatted (e.g., too long for the internal
95/// buffer) or if `CFE_ES_WriteToSysLog` returns an error.
96#[macro_export]
97macro_rules! log {
98 // Match a single literal string with no format arguments.
99 ($msg:literal) => {
100 $crate::log::syslog($msg)
101 };
102 // Match a format string plus arguments, like `println!`.
103 ($($arg:tt)*) => {{
104 use core::fmt::Write;
105 // The temporary buffer size is determined by the CFE mission config.
106 let mut buffer: heapless::String<{ $crate::log::SYSLOG_MAX_MSG_SIZE as usize }> = heapless::String::new();
107
108 // Attempt to format the arguments into our heapless string.
109 if write!(&mut buffer, $($arg)*).is_ok() {
110 $crate::log::syslog(&buffer)
111 } else {
112 // This error occurs if the formatted string is too large for the buffer.
113 Err($crate::error::CfsError::ValidationFailure)
114 }
115 }};
116}
117
118/// The maximum size of a single cFE System Log message, from cFE configuration.
119pub const SYSLOG_MAX_MSG_SIZE: usize = ffi::CFE_PLATFORM_ES_SYSTEM_LOG_SIZE as usize;
120
121/// Writes a message to the cFE system log.
122///
123/// This is useful for logging critical events, especially during initialization
124/// before Event Services (EVS) are available, or in error paths where EVS
125/// might fail.
126///
127/// The `log!` macro provides a more convenient, `println!`-like interface
128/// for this functionality.
129pub fn syslog(message: &str) -> Result<()> {
130 #[cfg(feature = "nos3")]
131 {
132 stdout_write(message);
133 stdout_write("\n");
134 }
135
136 let mut c_string = CString::<256>::new();
137 c_string
138 .extend_from_bytes(message.as_bytes())
139 .map_err(|_| CfsError::Osal(OsalError::NameTooLong))?;
140
141 check(unsafe { ffi::CFE_ES_WriteToSysLog(c_string.as_ptr()) })?;
142 Ok(())
143}
144
145#[cfg(feature = "nos3")]
146fn stdout_write(s: &str) {
147 extern "C" {
148 fn write(fd: i32, buf: *const u8, count: usize) -> isize;
149 }
150 unsafe { write(1, s.as_ptr(), s.len()); }
151}
152
153/// A macro to write a formatted string to the OSAL console (`OS_printf`).
154///
155/// This macro provides a `println!`-like interface for `OS_printf`, which is
156/// useful for debugging during development. It is a "fire-and-forget" macro
157/// that does not return a result.
158///
159/// # Usage
160///
161/// ```rust,ignore
162/// use libcfs::log::printf;
163///
164/// fn my_debug_function() {
165/// // Simple literal message
166/// printf!("Debug function entered.");
167///
168/// let value = 42;
169///
170/// // Formatted message
171/// printf!("The current debug value is: {}", value);
172/// }
173/// ```
174#[macro_export]
175macro_rules! printf {
176 // Match a single literal string with no format arguments.
177 ($msg:literal) => {
178 $crate::log::printf($msg)
179 };
180 // Match a format string plus arguments, like `println!`.
181 ($($arg:tt)*) => {{
182 use core::fmt::Write;
183 // The temporary buffer size is determined by the OSAL config.
184 let mut buffer: heapless::String<{ $crate::ffi::OS_BUFFER_SIZE as usize }> = heapless::String::new();
185
186 // Format the arguments. We ignore the result because printf is best-effort.
187 // If it fails, an empty or truncated string will be printed.
188 let _ = write!(&mut buffer, $($arg)*);
189
190 $crate::log::printf(&buffer);
191 }};
192}
193
194/// The maximum formatted EVS message size, from cFE mission config.
195pub const EVS_MAX_MSG_SIZE: usize = ffi::CFE_MISSION_EVS_MAX_MESSAGE_LENGTH as usize;
196
197/// Sends an informational EVS event (`EventType::Info`).
198///
199/// Event ID is derived from the call-site line number.
200///
201/// ```rust,ignore
202/// info!("system nominal")?;
203/// info!("processed {} packets", count)?;
204/// ```
205#[macro_export]
206macro_rules! info {
207 ($msg:literal) => {
208 $crate::cfe::evs::event::send(
209 line!() as u16,
210 $crate::cfe::evs::event::EventType::Info,
211 $msg,
212 )
213 };
214 ($($arg:tt)*) => {{
215 use core::fmt::Write;
216 let mut buf: heapless::String<{ $crate::log::EVS_MAX_MSG_SIZE }> = heapless::String::new();
217 if write!(&mut buf, $($arg)*).is_ok() {
218 $crate::cfe::evs::event::send(
219 line!() as u16,
220 $crate::cfe::evs::event::EventType::Info,
221 &buf,
222 )
223 } else {
224 Err($crate::error::CfsError::ValidationFailure)
225 }
226 }};
227}
228
229/// Sends a warning EVS event (`EventType::Error` — non-catastrophic).
230///
231/// cFS EVS has no dedicated warning level; this maps to
232/// `EventType::Error` which is defined as "not catastrophic."
233///
234/// ```rust,ignore
235/// warn!("temperature high: {} C", temp)?;
236/// ```
237#[macro_export]
238macro_rules! warn {
239 ($msg:literal) => {
240 $crate::cfe::evs::event::send(
241 line!() as u16,
242 $crate::cfe::evs::event::EventType::Error,
243 $msg,
244 )
245 };
246 ($($arg:tt)*) => {{
247 use core::fmt::Write;
248 let mut buf: heapless::String<{ $crate::log::EVS_MAX_MSG_SIZE }> = heapless::String::new();
249 if write!(&mut buf, $($arg)*).is_ok() {
250 $crate::cfe::evs::event::send(
251 line!() as u16,
252 $crate::cfe::evs::event::EventType::Error,
253 &buf,
254 )
255 } else {
256 Err($crate::error::CfsError::ValidationFailure)
257 }
258 }};
259}
260
261/// Sends a critical EVS event (`EventType::Critical`).
262///
263/// ```rust,ignore
264/// err!("{} failed", subsystem)?;
265/// ```
266#[macro_export]
267macro_rules! err {
268 ($msg:literal) => {
269 $crate::cfe::evs::event::send(
270 line!() as u16,
271 $crate::cfe::evs::event::EventType::Critical,
272 $msg,
273 )
274 };
275 ($($arg:tt)*) => {{
276 use core::fmt::Write;
277 let mut buf: heapless::String<{ $crate::log::EVS_MAX_MSG_SIZE }> = heapless::String::new();
278 if write!(&mut buf, $($arg)*).is_ok() {
279 $crate::cfe::evs::event::send(
280 line!() as u16,
281 $crate::cfe::evs::event::EventType::Critical,
282 &buf,
283 )
284 } else {
285 Err($crate::error::CfsError::ValidationFailure)
286 }
287 }};
288}