zephyr/
logging.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//! Rust logging in Zephyr
//!
//! There are a few config settings in Zephyr that affect how logging is done.  Zephyr has multiple
//! choices of front ends and backends for the logging.  We try to work with a few of these.
//!
//! There are various tradeoffs in terms of code size, and processing time that affect how logging
//! is done.  In addition to the tradeoffs in Zephyr, there are also tradeoffs in the Rust world,
//! especially when it comes to formatting.
//!
//! For now, any use of logging in Rust will go through the `log` crate.  This brings in the
//! overhead of string formatting.  For the most part, due to a semantic mismatch between how Rust
//! does string formatting, and how C does it, we will not defer logging, but generate log strings
//! that will be sent as full strings to the Zephyr logging system.
//!
//! - `CONFIG_LOG`: Global enable of logging in Zephyr.
//! - `CONFIG_LOG_MODE_MINIMAL`: Indicates a minimal printk type of logging.
//!
//! At this time, we will provide two loggers.  One is based on using the underlying printk
//! mechanism, and if enabled, will send log messages directly to printk.  This roughly corresponds
//! to the minimal logging mode of Zephyr, but also works if logging is entirely disabled.
//!
//! The other log backend uses the logging infrastructure to log simple messages to the C logger.
//! At this time, these require allocation for the string formatting, although the allocation will
//! generally be short lived.  A good future task will be to make the string formatter format
//! directly into the log buffer used by Zephyr.

use log::{LevelFilter, Log, SetLoggerError};

cfg_if::cfg_if! {
    if #[cfg(all(CONFIG_PRINTK,
                 any(not(CONFIG_LOG),
                     all(CONFIG_LOG, CONFIG_LOG_MODE_MINIMAL))))]
    {
        // If we either have no logging, or it is minimal, and we have printk, we can do the printk
        // logging handler.
        mod impl_printk;
        pub use impl_printk::set_logger;
    } else if #[cfg(all(CONFIG_LOG,
                        not(CONFIG_LOG_MODE_MINIMAL),
                        CONFIG_RUST_ALLOC))]
    {
        // Otherwise, if we have logging and allocation, and not minimal, we can use the Zephyr
        // logging backend.  Doing this without allocation is currently a TODO:
        mod impl_zlog;
        pub use impl_zlog::set_logger;
    } else {
        /// No lagging is possible, provide an empty handler that does nothing.
        ///
        /// It could be nice to print a message somewhere, but this is only available when there is
        /// no where we can send messages.
        pub unsafe fn set_logger() -> Result<(), SetLoggerError> {
            Ok(())
        }
    }
}

// The Rust logging system has different entry points based on whether or not we are on a target
// with atomic pointers.  We will provide a single function for this, which will be safe or unsafe
// depending on this.  The safety has to do with initialization order, and as long as this is called
// before any other threads run, it should be safe.
//
// TODO: Allow the default level to be set through Kconfig.
cfg_if::cfg_if! {
    if #[cfg(target_has_atomic = "ptr")] {
        unsafe fn set_logger_internal(logger: &'static dyn Log) -> Result<(), SetLoggerError> {
            log::set_logger(logger)?;
            log::set_max_level(LevelFilter::Info);
            Ok(())
        }
    } else {
        unsafe fn set_logger_internal(logger: &'static dyn Log) -> Result<(), SetLoggerError> {
            log::set_logger_racy(logger)?;
            log::set_max_level_racy(LevelFilter::Info);
            Ok(())
        }
    }
}