zephyr/
logging.rs

1//! Rust logging in Zephyr
2//!
3//! There are a few config settings in Zephyr that affect how logging is done.  Zephyr has multiple
4//! choices of front ends and backends for the logging.  We try to work with a few of these.
5//!
6//! There are various tradeoffs in terms of code size, and processing time that affect how logging
7//! is done.  In addition to the tradeoffs in Zephyr, there are also tradeoffs in the Rust world,
8//! especially when it comes to formatting.
9//!
10//! For now, any use of logging in Rust will go through the `log` crate.  This brings in the
11//! overhead of string formatting.  For the most part, due to a semantic mismatch between how Rust
12//! does string formatting, and how C does it, we will not defer logging, but generate log strings
13//! that will be sent as full strings to the Zephyr logging system.
14//!
15//! - `CONFIG_LOG`: Global enable of logging in Zephyr.
16//! - `CONFIG_LOG_MODE_MINIMAL`: Indicates a minimal printk type of logging.
17//!
18//! At this time, we will provide two loggers.  One is based on using the underlying printk
19//! mechanism, and if enabled, will send log messages directly to printk.  This roughly corresponds
20//! to the minimal logging mode of Zephyr, but also works if logging is entirely disabled.
21//!
22//! The other log backend uses the logging infrastructure to log simple messages to the C logger.
23//! At this time, these require allocation for the string formatting, although the allocation will
24//! generally be short lived.  A good future task will be to make the string formatter format
25//! directly into the log buffer used by Zephyr.
26
27use log::{LevelFilter, Log, SetLoggerError};
28
29cfg_if::cfg_if! {
30    if #[cfg(all(CONFIG_PRINTK,
31                 any(not(CONFIG_LOG),
32                     all(CONFIG_LOG, CONFIG_LOG_MODE_MINIMAL))))]
33    {
34        // If we either have no logging, or it is minimal, and we have printk, we can do the printk
35        // logging handler.
36        mod impl_printk;
37        pub use impl_printk::set_logger;
38    } else if #[cfg(all(CONFIG_LOG,
39                        not(CONFIG_LOG_MODE_MINIMAL),
40                        CONFIG_RUST_ALLOC))]
41    {
42        // Otherwise, if we have logging and allocation, and not minimal, we can use the Zephyr
43        // logging backend.  Doing this without allocation is currently a TODO:
44        mod impl_zlog;
45        pub use impl_zlog::set_logger;
46    } else {
47        /// No lagging is possible, provide an empty handler that does nothing.
48        ///
49        /// It could be nice to print a message somewhere, but this is only available when there is
50        /// no where we can send messages.
51        pub unsafe fn set_logger() -> Result<(), SetLoggerError> {
52            Ok(())
53        }
54    }
55}
56
57// The Rust logging system has different entry points based on whether or not we are on a target
58// with atomic pointers.  We will provide a single function for this, which will be safe or unsafe
59// depending on this.  The safety has to do with initialization order, and as long as this is called
60// before any other threads run, it should be safe.
61//
62// TODO: Allow the default level to be set through Kconfig.
63cfg_if::cfg_if! {
64    if #[cfg(target_has_atomic = "ptr")] {
65        unsafe fn set_logger_internal(logger: &'static dyn Log) -> Result<(), SetLoggerError> {
66            log::set_logger(logger)?;
67            log::set_max_level(LevelFilter::Info);
68            Ok(())
69        }
70    } else {
71        unsafe fn set_logger_internal(logger: &'static dyn Log) -> Result<(), SetLoggerError> {
72            log::set_logger_racy(logger)?;
73            log::set_max_level_racy(LevelFilter::Info);
74            Ok(())
75        }
76    }
77}