spdlog/
level.rs

1use std::{fmt, str::FromStr};
2
3use crate::Error;
4
5pub(crate) const LOG_LEVEL_NAMES: [&str; Level::count()] =
6    ["critical", "error", "warn", "info", "debug", "trace"];
7
8const LOG_LEVEL_SHORT_NAMES: [&str; Level::count()] = ["C", "E", "W", "I", "D", "T"];
9
10/// Represents log levels.
11///
12/// Typical usage:
13/// - specifying the `level` parameter of macro [`log!`];
14/// - comparing a `Level` to a [`LevelFilter`] through [`LevelFilter::test`].
15///
16/// # Note
17///
18/// Users should never cast variants of this enum to integers for persistent
19/// storage (e.g., configuration files), using [`Level::as_str`] instead,
20/// because integers corresponding to variants may change in the future.
21///
22/// Do **not** do this:
23/// ```
24/// # use spdlog::prelude::*;
25/// # fn save_to_config_file(_: u32) {}
26/// # let level: Level = Level::Info;
27/// let value = level as u32; // Never do numeric casting!
28///
29/// save_to_config_file(value);
30/// ```
31///
32/// Instead:
33/// ```
34/// # use spdlog::prelude::*;
35/// # fn save_to_config_file(_: &str) {}
36/// # let level: Level = Level::Info;
37/// let value = level.as_str();
38///
39/// save_to_config_file(value);
40/// ```
41///
42/// # Examples
43///
44/// ```
45/// use spdlog::prelude::*;
46///
47/// log!(Level::Info, "hello, world");
48/// ```
49///
50/// [`log!`]: crate::log!
51#[repr(u16)]
52#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
53pub enum Level {
54    /// Designates critical errors.
55    Critical = 0,
56    /// Designates very serious errors.
57    Error,
58    /// Designates hazardous situations.
59    Warn,
60    /// Designates useful information.
61    Info,
62    /// Designates lower priority information.
63    Debug,
64    /// Designates very low priority, often extremely verbose, information.
65    Trace,
66}
67
68#[cfg(feature = "serde")]
69impl serde::Serialize for Level {
70    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
71    where
72        S: serde::Serializer,
73    {
74        serializer.serialize_str(self.as_str())
75    }
76}
77
78impl Level {
79    #[must_use]
80    fn from_usize(u: usize) -> Option<Level> {
81        match u {
82            0 => Some(Level::Critical),
83            1 => Some(Level::Error),
84            2 => Some(Level::Warn),
85            3 => Some(Level::Info),
86            4 => Some(Level::Debug),
87            5 => Some(Level::Trace),
88            _ => None,
89        }
90    }
91
92    #[must_use]
93    const fn min_usize() -> usize {
94        Self::most_severe() as usize
95    }
96
97    #[must_use]
98    const fn max_usize() -> usize {
99        Self::most_verbose() as usize
100    }
101
102    #[must_use]
103    pub(crate) const fn count() -> usize {
104        Self::max_usize() + 1
105    }
106
107    /// Returns the most severe logging level.
108    #[must_use]
109    pub const fn most_severe() -> Level {
110        Level::Critical
111    }
112
113    /// Returns the most verbose logging level.
114    #[must_use]
115    pub const fn most_verbose() -> Level {
116        Level::Trace
117    }
118
119    /// Returns the string representation.
120    ///
121    /// This returns the same string as the `fmt::Display` implementation.
122    #[must_use]
123    pub fn as_str(&self) -> &'static str {
124        LOG_LEVEL_NAMES[*self as usize]
125    }
126
127    #[must_use]
128    pub(crate) fn as_short_str(&self) -> &'static str {
129        LOG_LEVEL_SHORT_NAMES[*self as usize]
130    }
131
132    /// Iterates through all logging levels.
133    ///
134    /// The order of iteration is from more severe to more verbose.
135    ///
136    /// # Examples
137    ///
138    /// ```
139    /// use spdlog::Level;
140    ///
141    /// let mut levels = Level::iter();
142    ///
143    /// assert_eq!(Some(Level::Critical), levels.next());
144    /// assert_eq!(Some(Level::Trace), levels.last());
145    /// ```
146    pub fn iter() -> impl Iterator<Item = Self> {
147        (Self::min_usize()..=Self::max_usize()).map(|i| Self::from_usize(i).unwrap())
148    }
149}
150
151#[cfg(feature = "log")]
152impl From<log::Level> for Level {
153    fn from(level: log::Level) -> Self {
154        match level {
155            log::Level::Error => Self::Error,
156            log::Level::Warn => Self::Warn,
157            log::Level::Info => Self::Info,
158            log::Level::Debug => Self::Debug,
159            log::Level::Trace => Self::Trace,
160        }
161    }
162}
163
164impl fmt::Display for Level {
165    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
166        f.write_str(self.as_str())
167    }
168}
169
170impl FromStr for Level {
171    type Err = Error;
172
173    fn from_str(level: &str) -> Result<Level, Self::Err> {
174        LOG_LEVEL_NAMES
175            .iter()
176            .position(|&name| name.eq_ignore_ascii_case(level))
177            .into_iter()
178            .map(|idx| Level::from_usize(idx).unwrap())
179            .next()
180            .ok_or_else(|| Error::ParseLevel(level.to_string()))
181    }
182}
183
184/// Represents log level logical filter conditions.
185///
186/// Use [`LevelFilter::test`] method to check if a [`Level`] satisfies the
187/// filter condition.
188#[repr(align(4))]
189#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
190pub enum LevelFilter {
191    /// Disables all levels.
192    Off,
193    /// Enables if the target level is equal to the filter level.
194    Equal(Level),
195    /// Enables if the target level is not equal to the filter level.
196    NotEqual(Level),
197    /// Enables if the target level is more severe than the filter level.
198    MoreSevere(Level),
199    /// Enables if the target level is more severe than or equal to the filter
200    /// level.
201    MoreSevereEqual(Level),
202    /// Enables if the target level is more verbose than the filter level.
203    MoreVerbose(Level),
204    /// Enables if the target level is more verbose than or equal to the filter
205    /// level.
206    MoreVerboseEqual(Level),
207    /// Enables all levels.
208    All,
209}
210
211impl LevelFilter {
212    /// Checks the given level if satisfies the filter condition.
213    #[deprecated(
214        since = "0.4.0",
215        note = "it may be removed in the future, use method `test()` instead"
216    )]
217    #[must_use]
218    pub fn compare(&self, level: Level) -> bool {
219        self.__test_const(level)
220    }
221
222    /// Checks the given level if satisfies the filter condition.
223    ///
224    /// # Examples
225    ///
226    /// ```
227    /// use spdlog::prelude::*;
228    ///
229    /// let level_filter = LevelFilter::MoreSevere(Level::Info);
230    ///
231    /// assert_eq!(level_filter.test(Level::Trace), false);
232    /// assert_eq!(level_filter.test(Level::Info), false);
233    /// assert_eq!(level_filter.test(Level::Warn), true);
234    /// assert_eq!(level_filter.test(Level::Error), true);
235    /// ```
236    #[must_use]
237    pub fn test(&self, level: Level) -> bool {
238        self.__test_const(level)
239    }
240
241    // Users should not use this function directly.
242    #[doc(hidden)]
243    #[must_use]
244    pub const fn __test_const(&self, level: Level) -> bool {
245        let level_num: u16 = level as u16;
246
247        match *self {
248            Self::Off => false,
249            Self::Equal(stored) => level_num == stored as u16,
250            Self::NotEqual(stored) => level_num != stored as u16,
251            Self::MoreSevere(stored) => level_num < stored as u16,
252            Self::MoreSevereEqual(stored) => level_num <= stored as u16,
253            Self::MoreVerbose(stored) => level_num > stored as u16,
254            Self::MoreVerboseEqual(stored) => level_num >= stored as u16,
255            Self::All => true,
256        }
257    }
258
259    #[must_use]
260    pub(crate) fn from_str_for_env(text: &str) -> Option<LevelFilter> {
261        if let Ok(level) = Level::from_str(text) {
262            Some(LevelFilter::MoreSevereEqual(level))
263        } else if text.eq_ignore_ascii_case("off") {
264            Some(LevelFilter::Off)
265        } else if text.eq_ignore_ascii_case("all") {
266            Some(LevelFilter::All)
267        } else {
268            None
269        }
270    }
271}
272
273#[cfg(feature = "log")]
274impl From<log::LevelFilter> for LevelFilter {
275    fn from(filter: log::LevelFilter) -> Self {
276        match filter {
277            log::LevelFilter::Off => Self::Off,
278            filter => Self::MoreSevereEqual(Level::from(filter.to_level().unwrap())),
279        }
280    }
281}
282
283#[cfg(test)]
284mod tests {
285    use std::mem::{align_of, size_of};
286
287    use super::*;
288    use crate::utils::const_assert;
289
290    const_assert!(atomic::Atomic::<Level>::is_lock_free());
291    const_assert!(atomic::Atomic::<LevelFilter>::is_lock_free());
292    const_assert!(size_of::<Level>() * 2 == size_of::<LevelFilter>());
293    const_assert!(align_of::<Level>() * 2 == align_of::<LevelFilter>());
294
295    #[test]
296    fn from_usize() {
297        assert_eq!(
298            Level::most_severe(),
299            Level::from_usize(Level::min_usize()).unwrap()
300        );
301
302        assert_eq!(
303            Level::most_verbose(),
304            Level::from_usize(Level::max_usize()).unwrap()
305        );
306    }
307
308    #[test]
309    fn from_str() {
310        fn to_random_case(input: &str) -> String {
311            input
312                .char_indices()
313                .map(|(i, char)| {
314                    if i % 2 != 0 {
315                        char.to_ascii_uppercase()
316                    } else {
317                        char.to_ascii_lowercase()
318                    }
319                })
320                .collect::<String>()
321        }
322
323        for (i, &name) in LOG_LEVEL_NAMES.iter().enumerate() {
324            let from_usize = Level::from_usize(i);
325            let from_str = (
326                Level::from_str(&name.to_lowercase()),
327                Level::from_str(&name.to_uppercase()),
328                Level::from_str(&to_random_case(name)),
329            );
330
331            assert_eq!(from_usize.unwrap(), from_str.0.unwrap());
332            assert_eq!(from_usize.unwrap(), from_str.1.unwrap());
333            assert_eq!(from_usize.unwrap(), from_str.2.unwrap());
334        }
335
336        assert!(Level::from_str("notexist").is_err());
337    }
338
339    #[test]
340    fn as_short_str() {
341        for (&name, &short_name) in LOG_LEVEL_NAMES.iter().zip(LOG_LEVEL_SHORT_NAMES.iter()) {
342            assert_eq!(
343                name.chars()
344                    .next()
345                    .unwrap()
346                    .to_ascii_uppercase()
347                    .to_string(),
348                short_name
349            );
350        }
351    }
352
353    #[test]
354    fn level_filter_from_str_for_env() {
355        assert_eq!(
356            LevelFilter::MoreSevereEqual(Level::Info),
357            LevelFilter::from_str_for_env("iNFo").unwrap()
358        );
359
360        assert_eq!(
361            LevelFilter::MoreSevereEqual(Level::Warn),
362            LevelFilter::from_str_for_env("wARn").unwrap()
363        );
364
365        assert_eq!(
366            LevelFilter::Off,
367            LevelFilter::from_str_for_env("oFf").unwrap()
368        );
369
370        assert_eq!(
371            LevelFilter::All,
372            LevelFilter::from_str_for_env("aLl").unwrap()
373        );
374    }
375
376    #[test]
377    fn iter() {
378        let mut iter = Level::iter();
379        assert_eq!(iter.next(), Some(Level::Critical));
380        assert_eq!(iter.next(), Some(Level::Error));
381        assert_eq!(iter.next(), Some(Level::Warn));
382        assert_eq!(iter.next(), Some(Level::Info));
383        assert_eq!(iter.next(), Some(Level::Debug));
384        assert_eq!(iter.next(), Some(Level::Trace));
385        assert_eq!(iter.next(), None);
386    }
387
388    #[test]
389    fn filter() {
390        assert!(!LevelFilter::Off.test(Level::Trace));
391        assert!(!LevelFilter::Off.test(Level::Critical));
392        assert!(!LevelFilter::Off.test(Level::Warn));
393
394        assert!(LevelFilter::Equal(Level::Error).test(Level::Error));
395        assert!(!LevelFilter::Equal(Level::Error).test(Level::Warn));
396        assert!(!LevelFilter::Equal(Level::Error).test(Level::Critical));
397
398        assert!(LevelFilter::NotEqual(Level::Error).test(Level::Trace));
399        assert!(LevelFilter::NotEqual(Level::Error).test(Level::Info));
400        assert!(!LevelFilter::NotEqual(Level::Error).test(Level::Error));
401
402        assert!(LevelFilter::MoreSevere(Level::Info).test(Level::Warn));
403        assert!(LevelFilter::MoreSevere(Level::Info).test(Level::Error));
404        assert!(!LevelFilter::MoreSevere(Level::Info).test(Level::Info));
405
406        assert!(LevelFilter::MoreSevereEqual(Level::Info).test(Level::Warn));
407        assert!(LevelFilter::MoreSevereEqual(Level::Info).test(Level::Info));
408        assert!(!LevelFilter::MoreSevereEqual(Level::Info).test(Level::Trace));
409
410        assert!(LevelFilter::MoreVerbose(Level::Error).test(Level::Warn));
411        assert!(LevelFilter::MoreVerbose(Level::Error).test(Level::Info));
412        assert!(!LevelFilter::MoreVerbose(Level::Error).test(Level::Error));
413
414        assert!(LevelFilter::MoreVerboseEqual(Level::Error).test(Level::Warn));
415        assert!(LevelFilter::MoreVerboseEqual(Level::Error).test(Level::Error));
416        assert!(!LevelFilter::MoreVerboseEqual(Level::Error).test(Level::Critical));
417
418        assert!(LevelFilter::All.test(Level::Trace));
419        assert!(LevelFilter::All.test(Level::Critical));
420        assert!(LevelFilter::All.test(Level::Error));
421    }
422
423    #[cfg(feature = "log")]
424    #[test]
425    fn filter_from_log() {
426        assert_eq!(LevelFilter::from(log::LevelFilter::Off), LevelFilter::Off);
427        assert_eq!(
428            LevelFilter::from(log::LevelFilter::Error),
429            LevelFilter::MoreSevereEqual(Level::Error)
430        );
431        assert_eq!(
432            LevelFilter::from(log::LevelFilter::Warn),
433            LevelFilter::MoreSevereEqual(Level::Warn)
434        );
435        assert_eq!(
436            LevelFilter::from(log::LevelFilter::Info),
437            LevelFilter::MoreSevereEqual(Level::Info)
438        );
439        assert_eq!(
440            LevelFilter::from(log::LevelFilter::Debug),
441            LevelFilter::MoreSevereEqual(Level::Debug)
442        );
443        assert_eq!(
444            LevelFilter::from(log::LevelFilter::Trace),
445            LevelFilter::MoreSevereEqual(Level::Trace)
446        );
447    }
448}