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}