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}