ostd/log/
mod.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! Kernel logging API.
4//!
5//! This module provides the logging facade for OSTD and all OSTD-based crates.
6//! It uses eight log levels matching the severity levels described in `syslog(2)`.
7//!
8//! # Setup: defining `__log_prefix`
9//!
10//! Every crate that uses the logging macros
11//! must define a `__log_prefix` macro at its crate root (`lib.rs`),
12//! before any `mod` declarations.
13//! This prefix is prepended to every log message from the crate.
14//!
15//! ```rust,ignore
16//! // Set this crate's log prefix for `ostd::log`.
17//! macro_rules! __log_prefix {
18//!     () => {
19//!         "virtio: "
20//!     };
21//! }
22//!
23//! mod device;   // all modules inherit the "virtio: " prefix
24//! ```
25//!
26//! It is recommended is to follow Linux's convention for log prefixes,
27//! which uses the lowercase module name, followed by `: `.
28//! For example: `"virtio: "`, `"pci: "`, `"uart: "`.
29//!
30//! # Quick start
31//!
32//! After defining `__log_prefix`, import the macros and use them:
33//!
34//! ```rust,ignore
35//! use ostd::prelude::*;
36//!
37//! info!("boot complete");
38//! warn!("feature X is not supported");
39//! ```
40//!
41//! # Log levels
42//!
43//! Eight severity levels are provided, matching `syslog(2)`:
44//!
45//! | Level   | Value | Meaning                      |
46//! |---------|-------|------------------------------|
47//! | Emerg   | 0     | System is unusable           |
48//! | Alert   | 1     | Action must be taken         |
49//! | Crit    | 2     | Critical conditions          |
50//! | Error   | 3     | Error conditions             |
51//! | Warning | 4     | Warning conditions           |
52//! | Notice  | 5     | Normal but significant       |
53//! | Info    | 6     | Informational                |
54//! | Debug   | 7     | Debug-level messages         |
55//!
56//! ```rust,ignore
57//! use ostd::prelude::*;
58//!
59//! emerg!("system is going down");
60//! alert!("action required immediately");
61//! crit!("critical failure in subsystem");
62//! error!("operation failed: {}", err);
63//! warn!("deprecated feature used");
64//! notice!("configuration change applied");
65//! info!("boot complete");
66//! debug!("variable x = {:?}", x);
67//! ```
68//!
69//! # `log` crate bridge
70//!
71//! A bridge forwards messages from third-party crates
72//! that use the [`log`](https://docs.rs/log) crate (e.g., `smoltcp`)
73//! to the OSTD logger.
74//! First-party code should use OSTD's macros directly.
75//!
76//! # Per-module prefix overrides
77//!
78//! A subsystem module can override the crate-level prefix
79//! by defining its own `__log_prefix` at the top of its `mod.rs`,
80//! before any `mod child;` declarations.
81//! Child modules inherit the override via textual scoping:
82//!
83//! ```rust,ignore
84//! // Set this module's log prefix for `ostd::log`.
85//! macro_rules! __log_prefix {
86//!     () => {
87//!         "iommu: "
88//!     };
89//! }
90//!
91//! mod fault;      // inherits "iommu: " prefix
92//! mod registers;  // inherits "iommu: " prefix
93//! ```
94//!
95//! # Limitations
96//!
97//! ## No attributes on `__log_prefix` definitions
98//!
99//! Do not put `#[rustfmt::skip]` or any other attribute
100//! on `__log_prefix` definitions.
101//! Rust treats attributed `macro_rules!` items as "macro-expanded,"
102//! which triggers E0659 ambiguity with definitions at other scopes.
103//! See the design doc in `log/macros.rs` for the full explanation.
104//!
105//! # Backend
106//!
107//! An OSTD-based kernel can register a custom [`Log`] implementation via [`inject_logger`].
108//! Before a backend is registered, messages are printed through the early-boot console.
109
110mod bridge;
111mod level;
112mod logger;
113mod macros;
114
115use self::bridge::LogCrateBridge;
116pub use self::{
117    level::{Level, LevelFilter},
118    logger::{
119        __write_log_record, Log, Record, STATIC_MAX_LEVEL, inject_logger, max_level, set_max_level,
120    },
121};
122
123/// Initializes the OSTD logging subsystem.
124///
125/// Parses the `ostd.log_level` kernel command line parameter, sets the
126/// runtime max level, and registers the `log` crate bridge.
127pub(crate) fn init() {
128    let filter = parse_log_level_from_cmdline().unwrap_or(LevelFilter::Off);
129    set_max_level(filter);
130
131    static BRIDGE: LogCrateBridge = LogCrateBridge;
132    let _ = ::log::set_logger(&BRIDGE);
133}
134
135fn parse_log_level_from_cmdline() -> Option<LevelFilter> {
136    let kcmdline = crate::boot::EARLY_INFO.get()?.kernel_cmdline;
137
138    let value = kcmdline
139        .split(' ')
140        .find(|arg| arg.starts_with("ostd.log_level="))
141        .map(|arg| arg.split('=').next_back().unwrap_or_default())?;
142
143    parse_level_str(value)
144}
145
146/// Parses a log level string into a [`LevelFilter`].
147///
148/// Accepts: `"off"`, `"emerg"`, `"alert"`, `"crit"`, `"error"`,
149/// `"warn"` / `"warning"`, `"notice"`, `"info"`, `"debug"`.
150/// Returns `None` for unrecognized strings.
151fn parse_level_str(s: &str) -> Option<LevelFilter> {
152    match s {
153        "off" => Some(LevelFilter::Off),
154        "emerg" => Some(LevelFilter::Emerg),
155        "alert" => Some(LevelFilter::Alert),
156        "crit" => Some(LevelFilter::Crit),
157        "error" => Some(LevelFilter::Error),
158        "warn" | "warning" => Some(LevelFilter::Warning),
159        "notice" => Some(LevelFilter::Notice),
160        "info" => Some(LevelFilter::Info),
161        "debug" => Some(LevelFilter::Debug),
162        _ => None,
163    }
164}
165
166#[cfg(ktest)]
167mod test {
168    use super::*;
169    use crate::prelude::*;
170
171    #[ktest]
172    fn parse_level_str_valid() {
173        assert_eq!(parse_level_str("off"), Some(LevelFilter::Off));
174        assert_eq!(parse_level_str("emerg"), Some(LevelFilter::Emerg));
175        assert_eq!(parse_level_str("alert"), Some(LevelFilter::Alert));
176        assert_eq!(parse_level_str("crit"), Some(LevelFilter::Crit));
177        assert_eq!(parse_level_str("error"), Some(LevelFilter::Error));
178        assert_eq!(parse_level_str("warn"), Some(LevelFilter::Warning));
179        assert_eq!(parse_level_str("warning"), Some(LevelFilter::Warning));
180        assert_eq!(parse_level_str("notice"), Some(LevelFilter::Notice));
181        assert_eq!(parse_level_str("info"), Some(LevelFilter::Info));
182        assert_eq!(parse_level_str("debug"), Some(LevelFilter::Debug));
183    }
184
185    #[ktest]
186    fn parse_level_str_invalid() {
187        assert_eq!(parse_level_str("trace"), None);
188        assert_eq!(parse_level_str(""), None);
189        assert_eq!(parse_level_str("INFO"), None);
190        assert_eq!(parse_level_str("garbage"), None);
191    }
192}