ostd/
logger.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! Logger injection.
4//!
5//! OSTD allows its client to inject a custom implementation of logger.
6//! If no such logger is injected,
7//! then OSTD falls back to a built-in logger that
8//! simply dumps all log records with [`crate::console::early_print`].
9//!
10//! OSTD's logger facility relies on the [log] crate.
11//! Both an OSTD client and OSTD itself use the macros from the `log` crate
12//! such as `error`, `info`, and `debug` to emit log records.
13//! The injected logger is required to implement the [`log::Log`] trait.
14//!
15//! [log]: https://docs.rs/log
16
17use core::str::FromStr;
18
19use log::{LevelFilter, Metadata, Record};
20use spin::Once;
21
22use crate::boot::EARLY_INFO;
23
24/// Injects a logger.
25///
26/// This method can be called at most once; calling it more than once has no effect.
27///
28/// # Requirements
29///
30/// As the logger may be invoked in stringent situations,
31/// such as an interrupt handler, an out-of-memory handler, or a panic handler,
32/// a logger should be implemented to be
33/// _short_ (simple and non-sleeping) and
34/// _heapless_ (not trigger heap allocations).
35/// Failing to do so may cause the kernel to panic or deadlock.
36pub fn inject_logger(new_logger: &'static dyn log::Log) {
37    LOGGER.backend.call_once(|| new_logger);
38}
39
40/// Initializes the logger. Users should avoid using the log macros before this function is called.
41pub(crate) fn init() {
42    let level = get_log_level().unwrap_or(LevelFilter::Off);
43    log::set_max_level(level);
44    log::set_logger(&LOGGER).unwrap();
45}
46
47static LOGGER: Logger = Logger::new();
48
49struct Logger {
50    backend: Once<&'static dyn log::Log>,
51}
52
53impl Logger {
54    const fn new() -> Self {
55        Self {
56            backend: Once::new(),
57        }
58    }
59}
60
61impl log::Log for Logger {
62    fn enabled(&self, metadata: &Metadata) -> bool {
63        if let Some(logger) = self.backend.get() {
64            return logger.enabled(metadata);
65        };
66
67        // Default implementation.
68        true
69    }
70
71    fn log(&self, record: &Record) {
72        if let Some(logger) = self.backend.get() {
73            return logger.log(record);
74        };
75
76        // Default implementation.
77        let level = record.level();
78        crate::console::early_print(format_args!("{}: {}\n", level, record.args()));
79    }
80
81    fn flush(&self) {
82        if let Some(logger) = self.backend.get() {
83            logger.flush();
84        };
85    }
86}
87
88fn get_log_level() -> Option<LevelFilter> {
89    let kcmdline = EARLY_INFO.get().unwrap().kernel_cmdline;
90
91    // Although OSTD is agnostic of the parsing of the kernel command line,
92    // the logger assumes that it follows the Linux kernel command line format.
93    // We search for the `ostd.log_level=ARGUMENT` pattern in string.
94    let value = kcmdline
95        .split(' ')
96        .find(|arg| arg.starts_with("ostd.log_level="))
97        .map(|arg| arg.split('=').next_back().unwrap_or_default())?;
98
99    LevelFilter::from_str(value).ok()
100}