cortex_m_log/
log.rs

1//! Integration with [log](https://docs.rs/crate/log)
2//!
3//! As embedded development implies `#[no_std]` we cannot box
4//! our logger so instead simplest approach would be to declare our logger static
5//!
6//! ```rust
7//! use log::info;
8//! use cortex_m_log::log::{Logger, init};
9//! use cortex_m_log::printer::Dummy;
10//!
11//! static LOGGER: Logger<Dummy> = Logger {
12//!     inner: Dummy::new(),
13//!     level: log::LevelFilter::Off
14//! };
15//!
16//! fn main() {
17//!     init(&LOGGER).expect("To set logger");
18//!
19//!     info!("Starting my cute program");
20//! }
21//! ```
22//!
23//! Of course since there is no `const-fn` and some printers require
24//! Initialization function we need some better way.
25//!
26//! One way to do it would be to trick compiler by changing lifetime of stack
27//! allocated logger to static, obviously unsafe.
28//!
29//! ```rust,no_run
30//! use core::mem;
31//!
32//! use cortex_m_log::log::{Logger, trick_init};
33//! use cortex_m_log::printer::semihosting;
34//! use cortex_m_log::modes::InterruptOk;
35//!
36//! let logger = Logger {
37//!     //Uses semihosting as destination with no interrupt control.
38//!     inner: semihosting::InterruptOk::<_>::stdout().expect("Get Semihosting stdout"),
39//!     level: log::LevelFilter::Info
40//! };
41//! //Haha trust me, it is safe ;)
42//! //As long as logger is not dropped....
43//! unsafe {
44//!     let _ = trick_init(&logger);
45//! }
46//! ```
47//!
48//! Obviously it is UB to drop logger after that and use any of log's macros
49use core::mem;
50use core::marker;
51use crate::printer::Printer;
52
53///Simple Logger implementation
54pub struct Logger<P: Printer + marker::Send + marker::Sync> {
55    ///Printer implementation
56    pub inner: P,
57    ///Log level
58    pub level: log::LevelFilter,
59}
60
61impl<P: Printer + marker::Send + marker::Sync> log::Log for Logger<P> {
62    fn enabled(&self, metadata: &log::Metadata) -> bool {
63        metadata.level() <= self.level
64    }
65
66    fn log(&self, record: &log::Record) {
67        if self.enabled(record.metadata()) {
68            //Pretend we're the Cell
69            let inner = &self.inner as *const P as *mut P;
70            let inner = unsafe { &mut *inner };
71
72            inner.print(format_args!("{:<5} {}:{} - {}\n", record.level(), record.file().unwrap_or("UNKNOWN"), record.line().unwrap_or(0), record.args()))
73        }
74    }
75
76    fn flush(&self) {
77    }
78}
79
80///Initialize logging facilities.
81///
82///Currently lacks a nice way to set global logger so user
83///must himself guarantee static lifetime
84///
85///## Atomic availability notes
86///
87///On some targets, only limited set of atomic ops are available.
88///In this case, this function initializes logger only once
89pub fn init<P: Printer + marker::Send + marker::Sync>(logger: &'static Logger<P>) -> Result<(), log::SetLoggerError> {
90    log::set_max_level(logger.level);
91    #[cfg(feature = "atomic_cas")]
92    {
93        log::set_logger(logger)
94    }
95    #[cfg(not(feature = "atomic_cas"))]
96    {
97        use core::sync::atomic::{Ordering, AtomicBool};
98        static INIT: AtomicBool = AtomicBool::new(false);
99
100        let is_init = INIT.load(Ordering::Acquire);
101        INIT.store(true, Ordering::Release);
102
103        match is_init {
104            true => Ok(()),
105            false => {
106                unsafe {
107                    log::set_logger_racy(logger)
108                }
109            }
110        }
111    }
112}
113
114#[inline]
115///Performs init by tricking compiler into beliving that `&Logger` is static.
116///
117///This is unsafe and it is up to you to ensure that reference
118///will live longer that any attempts to access it
119pub unsafe fn trick_init<P: Printer + marker::Send + marker::Sync + 'static>(logger: &Logger<P>) -> Result<(), log::SetLoggerError> {
120    let logger: &'static Logger<P> = mem::transmute(logger);
121    init(logger)
122}