ostd/log/macros.rs
1// SPDX-License-Identifier: MPL-2.0
2
3//! Logging macros.
4//!
5//! Contains the core [`log!`](crate::log!) macro and
6//! level-specific wrappers (`info!`, `warn!`, etc.).
7//! All are `#[macro_export]` so downstream crates can access them
8//! via `use ostd::info;` etc.
9
10/// Logs a message at the [`Emerg`] level.
11///
12/// [`Emerg`]: crate::log::Level::Emerg
13#[macro_export]
14macro_rules! emerg {
15 ($($arg:tt)+) => { $crate::log!($crate::log::Level::Emerg, $($arg)+) };
16}
17
18/// Logs a message at the [`Alert`] level.
19///
20/// [`Alert`]: crate::log::Level::Alert
21#[macro_export]
22macro_rules! alert {
23 ($($arg:tt)+) => { $crate::log!($crate::log::Level::Alert, $($arg)+) };
24}
25
26/// Logs a message at the [`Crit`] level.
27///
28/// [`Crit`]: crate::log::Level::Crit
29#[macro_export]
30macro_rules! crit {
31 ($($arg:tt)+) => { $crate::log!($crate::log::Level::Crit, $($arg)+) };
32}
33
34/// Logs a message at the [`Error`] level.
35///
36/// [`Error`]: crate::log::Level::Error
37#[macro_export]
38macro_rules! error {
39 ($($arg:tt)+) => { $crate::log!($crate::log::Level::Error, $($arg)+) };
40}
41
42/// Logs a message at the [`Warning`] level.
43///
44/// [`Warning`]: crate::log::Level::Warning
45#[macro_export]
46macro_rules! warn {
47 ($($arg:tt)+) => { $crate::log!($crate::log::Level::Warning, $($arg)+) };
48}
49
50/// Logs a message at the [`Notice`] level.
51///
52/// [`Notice`]: crate::log::Level::Notice
53#[macro_export]
54macro_rules! notice {
55 ($($arg:tt)+) => { $crate::log!($crate::log::Level::Notice, $($arg)+) };
56}
57
58/// Logs a message at the [`Info`] level.
59///
60/// [`Info`]: crate::log::Level::Info
61#[macro_export]
62macro_rules! info {
63 ($($arg:tt)+) => { $crate::log!($crate::log::Level::Info, $($arg)+) };
64}
65
66/// Logs a message at the [`Debug`] level.
67///
68/// [`Debug`]: crate::log::Level::Debug
69#[macro_export]
70macro_rules! debug {
71 ($($arg:tt)+) => { $crate::log!($crate::log::Level::Debug, $($arg)+) };
72}
73
74/// Returns `true` if a message at the given level would be logged.
75///
76/// Checks both the compile-time [`STATIC_MAX_LEVEL`] and the runtime
77/// [`max_level`] filters.
78///
79/// [`STATIC_MAX_LEVEL`]: crate::log::STATIC_MAX_LEVEL
80/// [`max_level`]: crate::log::max_level
81#[macro_export]
82macro_rules! log_enabled {
83 ($level:expr) => {{
84 let __level: $crate::log::Level = $level;
85 $crate::log::STATIC_MAX_LEVEL.is_enabled(__level)
86 && $crate::log::max_level().is_enabled(__level)
87 }};
88}
89
90/// Logs a message at the given level.
91///
92/// This is the core logging macro.
93/// All level-specific macros delegate to it.
94///
95/// The macro references a bare `__log_prefix!()` which resolves at
96/// the call site via Rust's textual macro scoping.
97/// The prefix is passed as a separate field on the [`Record`]
98/// rather than concatenated into the format string,
99/// so implicit format captures (`info!("{var}")`) work normally.
100///
101/// [`Record`]: crate::log::Record
102///
103/// # Examples
104///
105/// ```rust,ignore
106/// use ostd::log::Level;
107/// ostd::log!(Level::Warning, "unsupported");
108/// ostd::log!(Level::Info, "value = {}", x);
109/// ostd::log!(Level::Debug, "{name} started");
110/// ```
111#[macro_export]
112macro_rules! log {
113 ($level:expr, $($arg:tt)+) => {{
114 // `const` is intentional: it enables compile-time dead code
115 // elimination so that log calls above `STATIC_MAX_LEVEL` are
116 // removed entirely.
117 const __LEVEL: $crate::log::Level = $level;
118 if $crate::log_enabled!(__LEVEL) {
119 $crate::log::__write_log_record(&$crate::log::Record::new(
120 __LEVEL,
121 __log_prefix!(),
122 format_args!($($arg)+),
123 module_path!(),
124 file!(),
125 line!(),
126 ));
127 }
128 }};
129}
130
131/// # How the `__log_prefix` mechanism works
132///
133/// The per-module prefix mechanism relies on Rust's textual
134/// `macro_rules!` scoping rules. This document explains these rules,
135/// the key constraints on `__log_prefix` definitions,
136/// and why certain seemingly-reasonable alternatives don't work.
137///
138/// ## How the prefix reaches the output
139///
140/// The `log!` macro passes `__log_prefix!()` as a separate
141/// `&'static str` field on [`Record`](crate::log::Record),
142/// not as part of the `format_args!()` string.
143/// The logger backend prepends it when formatting the message.
144/// This avoids using `concat!()` inside `format_args!()`,
145/// which would disable Rust's implicit format captures
146/// (e.g., `info!("{var}")`).
147///
148/// ## Textual scoping of `macro_rules!`
149///
150/// A `macro_rules!` definition is visible to all items that appear
151/// **after** it in the same file,
152/// including file-backed child modules declared via `mod child;`:
153///
154/// ```rust,ignore
155/// // lib.rs
156/// macro_rules! __log_prefix { () => { "" }; }
157/// mod sub; // sub.rs can use __log_prefix!()
158/// ```
159///
160/// A `macro_rules!` in an inner scope **shadows** one from an outer
161/// scope. This enables per-module overrides:
162///
163/// ```rust,ignore
164/// // lib.rs
165/// macro_rules! __log_prefix { () => { "" }; }
166/// mod sub;
167///
168/// // sub/mod.rs
169/// macro_rules! __log_prefix { () => { "sub: " }; }
170/// mod child; // child.rs sees "sub: ", not ""
171/// ```
172///
173/// ## How `__log_prefix!()` resolves through the macro chain
174///
175/// The `log!` macro contains a bare `__log_prefix!()` reference
176/// (without `$crate::`). Bare names in `macro_rules!` expansions
177/// resolve at the **call site**, not the definition site.
178/// So when `info!("msg")` expands to
179/// `$crate::log!(Level::Info, "msg")`,
180/// the `__log_prefix!()` inside `log!` resolves at the site where
181/// `info!()` was written — finding whichever `__log_prefix` is in
182/// scope there (either the crate-root default or a module override).
183///
184/// ## Why `__log_prefix` must be at the crate root
185///
186/// The `__log_prefix` default must be defined at the crate root
187/// (`lib.rs`), before any `mod` declarations, so that every module
188/// in the crate inherits it via textual scoping.
189/// Without it, any `info!()` call would fail with
190/// "cannot find macro `__log_prefix`."
191///
192/// ## Why `__log_prefix` cannot be made more user-friendly
193///
194/// The raw `macro_rules! __log_prefix { ... }` boilerplate is admittedly
195/// ugly. Two natural approaches to beautify it were explored and both
196/// fail due to the same underlying Rust limitation.
197///
198/// **The underlying rule:** Rust's macro name resolution tracks whether
199/// a `macro_rules!` item was directly written in source code or produced
200/// by some form of macro expansion (including attribute processing).
201/// Two definitions of the same name at different scopes are only allowed
202/// to shadow each other if they are at the same "expansion level."
203/// If one is directly written and the other is "macro-expanded,"
204/// Rust reports E0659 (ambiguous).
205///
206/// ### Attempt 1: keep it one-line with `#[rustfmt::skip]`
207///
208/// Without `#[rustfmt::skip]`, `rustfmt` expands the definition to
209/// multiple lines. It would be nice to write:
210///
211/// ```rust,ignore
212/// #[rustfmt::skip]
213/// macro_rules! __log_prefix { () => { "sub: " }; }
214/// ```
215///
216/// But `#[rustfmt::skip]` (or any attribute) on a `macro_rules!` item
217/// causes Rust to treat the item as "macro-expanded."
218/// When the crate root has a directly-written default and a submodule
219/// has an attributed override, the two are at different expansion levels
220/// and Rust reports E0659:
221///
222/// ```rust,ignore
223/// // lib.rs — directly written
224/// macro_rules! __log_prefix { () => { "" }; }
225///
226/// // sub/mod.rs — attributed → "macro-expanded" → AMBIGUOUS
227/// #[rustfmt::skip]
228/// macro_rules! __log_prefix { () => { "sub: " }; }
229/// ```
230///
231/// ### Attempt 2: hide behind a `set_log_prefix!` wrapper macro
232///
233/// A wrapper like `ostd::set_log_prefix!("iommu")` that expands to
234/// `macro_rules! __log_prefix { ... }` would be much more ergonomic.
235/// However, the expanded `macro_rules!` item is "macro-expanded"
236/// (it was produced by expanding `set_log_prefix!`).
237/// Two such definitions at different scopes — one from the crate root's
238/// `set_log_prefix!` and one from a module's `set_log_prefix!` — are
239/// always ambiguous (E0659), even though both originate from the same
240/// wrapper macro.
241///
242/// ### Conclusion
243///
244/// The only way to get clean shadowing between scopes is for both the
245/// default and the override to be **directly written** `macro_rules!`
246/// items with no attributes and no wrapper macros.
247mod about_log_prefix_macro {}