ostd/log/
level.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! Log level and level filter types.
4
5use core::fmt;
6
7/// Kernel log level, matching the severity levels described in `syslog(2)`.
8///
9/// # Ordering
10///
11/// Levels are ordered by severity: `Emerg < Alert < ... < Debug`.
12/// `a < b` means `a` is more severe than `b`.
13///
14/// Higher severity is assigned lower numeric value.
15/// For example,
16/// `Level::Emerg` has the smallest value of 0 and means the highest severity.
17/// On the end of the spectrum,
18/// `Level::Debug` has the largest value of 7 and means the lowest severity.
19///
20/// # Mapping from Linux's logging levels
21///
22/// The numeric values of `Level::Xxx` are assigned to those of their Linux counterparts.
23///
24/// ```text
25/// LOGLEVEL_EMERG      0   /* system is unusable */
26/// LOGLEVEL_ALERT      1   /* action must be taken immediately */
27/// LOGLEVEL_CRIT       2   /* critical conditions */
28/// LOGLEVEL_ERR        3   /* error conditions */
29/// LOGLEVEL_WARNING    4   /* warning conditions */
30/// LOGLEVEL_NOTICE     5   /* normal but significant condition */
31/// LOGLEVEL_INFO       6   /* informational */
32/// LOGLEVEL_DEBUG      7   /* debug-level messages */
33/// ```
34///
35/// # Mapping from the `log` crate
36///
37/// | `log::Level` | `ostd::log::Level` | Notes |
38/// |--------------|--------------------| ------|
39/// | Error        | Error (3)          | |
40/// | Warn         | Warning (4)        | |
41/// | Info         | Info (6)           | |
42/// | Debug        | Debug (7)          | |
43/// | Trace        | Debug (7)          | Bridge only; no `trace!` macro in OSTD |
44#[repr(u8)]
45#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
46pub enum Level {
47    /// System is unusable.
48    Emerg = 0,
49    /// Action must be taken immediately.
50    Alert = 1,
51    /// Critical conditions.
52    Crit = 2,
53    /// Error conditions.
54    Error = 3,
55    /// Warning conditions.
56    Warning = 4,
57    /// Normal but significant condition.
58    Notice = 5,
59    /// Informational.
60    Info = 6,
61    /// Debug-level messages.
62    Debug = 7,
63}
64
65impl Level {
66    /// Creates a `Level` from a numeric value (0--7).
67    ///
68    /// Values > 7 are clamped to `Debug`.
69    pub const fn from_u8(val: u8) -> Self {
70        match val {
71            0 => Self::Emerg,
72            1 => Self::Alert,
73            2 => Self::Crit,
74            3 => Self::Error,
75            4 => Self::Warning,
76            5 => Self::Notice,
77            6 => Self::Info,
78            _ => Self::Debug,
79        }
80    }
81}
82
83impl fmt::Display for Level {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        f.pad(match self {
86            Level::Emerg => "EMERG",
87            Level::Alert => "ALERT",
88            Level::Crit => "CRIT",
89            Level::Error => "ERROR",
90            Level::Warning => "WARN",
91            Level::Notice => "NOTICE",
92            Level::Info => "INFO",
93            Level::Debug => "DEBUG",
94        })
95    }
96}
97
98/// A filter for log levels.
99///
100/// `LevelFilter::from_level(level)` includes that level and all more-severe levels.
101/// `LevelFilter::Off` disables all logging.
102///
103/// The filtering rule: a level passes when `(filter as u8) > (level as u8)`.
104///
105/// ```text
106/// LevelFilter::Off(0)     -> nothing passes
107/// LevelFilter::Error(4)   -> Emerg(0), Alert(1), Crit(2), Error(3)
108/// LevelFilter::Debug(8)   -> everything
109/// ```
110#[repr(u8)]
111#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
112pub enum LevelFilter {
113    /// All logging disabled.
114    Off = 0,
115    /// Enable Emerg only.
116    Emerg = 1,
117    /// Enable Emerg and Alert.
118    Alert = 2,
119    /// Enable Emerg through Crit.
120    Crit = 3,
121    /// Enable Emerg through Error.
122    Error = 4,
123    /// Enable Emerg through Warning.
124    Warning = 5,
125    /// Enable Emerg through Notice.
126    Notice = 6,
127    /// Enable Emerg through Info.
128    Info = 7,
129    /// Enable all levels.
130    Debug = 8,
131}
132
133impl LevelFilter {
134    /// Returns `true` if `level` passes this filter.
135    #[inline]
136    pub const fn is_enabled(self, level: Level) -> bool {
137        (self as u8) > (level as u8)
138    }
139
140    /// Constructs a filter that enables `level` and all more-severe levels.
141    pub const fn from_level(level: Level) -> Self {
142        match level {
143            Level::Emerg => Self::Emerg,
144            Level::Alert => Self::Alert,
145            Level::Crit => Self::Crit,
146            Level::Error => Self::Error,
147            Level::Warning => Self::Warning,
148            Level::Notice => Self::Notice,
149            Level::Info => Self::Info,
150            Level::Debug => Self::Debug,
151        }
152    }
153
154    /// Creates a `LevelFilter` from a numeric value (0--8).
155    ///
156    /// Values > 8 are clamped to `Debug`.
157    pub const fn from_u8(val: u8) -> Self {
158        match val {
159            0 => Self::Off,
160            1 => Self::Emerg,
161            2 => Self::Alert,
162            3 => Self::Crit,
163            4 => Self::Error,
164            5 => Self::Warning,
165            6 => Self::Notice,
166            7 => Self::Info,
167            _ => Self::Debug,
168        }
169    }
170}
171
172#[cfg(ktest)]
173mod test {
174    use super::*;
175    use crate::prelude::*;
176
177    #[ktest]
178    fn level_ordering() {
179        assert!(Level::Emerg < Level::Alert);
180        assert!(Level::Alert < Level::Crit);
181        assert!(Level::Crit < Level::Error);
182        assert!(Level::Error < Level::Warning);
183        assert!(Level::Warning < Level::Notice);
184        assert!(Level::Notice < Level::Info);
185        assert!(Level::Info < Level::Debug);
186    }
187
188    #[ktest]
189    fn level_filter_enabled() {
190        assert!(!LevelFilter::Off.is_enabled(Level::Emerg));
191        assert!(LevelFilter::Emerg.is_enabled(Level::Emerg));
192        assert!(!LevelFilter::Emerg.is_enabled(Level::Alert));
193        assert!(LevelFilter::Error.is_enabled(Level::Error));
194        assert!(LevelFilter::Error.is_enabled(Level::Crit));
195        assert!(!LevelFilter::Error.is_enabled(Level::Warning));
196        assert!(LevelFilter::Debug.is_enabled(Level::Debug));
197    }
198
199    #[ktest]
200    fn level_from_u8_clamping() {
201        assert_eq!(Level::from_u8(0), Level::Emerg);
202        assert_eq!(Level::from_u8(7), Level::Debug);
203        assert_eq!(Level::from_u8(255), Level::Debug);
204    }
205
206    #[ktest]
207    fn level_filter_from_level() {
208        assert_eq!(LevelFilter::from_level(Level::Error), LevelFilter::Error);
209        assert_eq!(LevelFilter::from_level(Level::Debug), LevelFilter::Debug);
210    }
211}