spdlog/
level.rs

1use std::{
2    fmt::{self, Debug},
3    str::FromStr,
4    sync::atomic::Ordering,
5};
6
7use crate::Error;
8
9pub(crate) const LOG_LEVEL_NAMES: [&str; Level::count()] =
10    ["critical", "error", "warn", "info", "debug", "trace"];
11
12const LOG_LEVEL_SHORT_NAMES: [&str; Level::count()] = ["C", "E", "W", "I", "D", "T"];
13
14/// Represents log levels.
15///
16/// Typical usage:
17/// - specifying the `level` parameter of macro [`log!`];
18/// - comparing a `Level` to a [`LevelFilter`] through [`LevelFilter::test`].
19///
20/// # Note
21///
22/// Users should never cast variants of this enum to integers for persistent
23/// storage (e.g., configuration files), using [`Level::as_str`] instead,
24/// because integers corresponding to variants may change in the future.
25///
26/// Do **not** do this:
27/// ```
28/// # use spdlog::prelude::*;
29/// # fn save_to_config_file(_: u32) {}
30/// # let level: Level = Level::Info;
31/// let value = level as u32; // Never do numeric casting!
32///
33/// save_to_config_file(value);
34/// ```
35///
36/// Instead:
37/// ```
38/// # use spdlog::prelude::*;
39/// # fn save_to_config_file(_: &str) {}
40/// # let level: Level = Level::Info;
41/// let value = level.as_str();
42///
43/// save_to_config_file(value);
44/// ```
45///
46/// # Examples
47///
48/// ```
49/// use spdlog::prelude::*;
50///
51/// log!(Level::Info, "hello, world");
52/// ```
53///
54/// [`log!`]: crate::log!
55#[repr(u16)]
56#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, bytemuck::NoUninit)]
57pub enum Level {
58    /// Designates critical errors.
59    Critical = 0,
60    /// Designates very serious errors.
61    Error,
62    /// Designates hazardous situations.
63    Warn,
64    /// Designates useful information.
65    Info,
66    /// Designates lower priority information.
67    Debug,
68    /// Designates very low priority, often extremely verbose, information.
69    Trace,
70}
71
72#[cfg(feature = "serde")]
73impl serde::Serialize for Level {
74    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
75    where
76        S: serde::Serializer,
77    {
78        serializer.serialize_str(self.as_str())
79    }
80}
81
82impl Level {
83    #[must_use]
84    fn from_usize(u: usize) -> Option<Level> {
85        match u {
86            0 => Some(Level::Critical),
87            1 => Some(Level::Error),
88            2 => Some(Level::Warn),
89            3 => Some(Level::Info),
90            4 => Some(Level::Debug),
91            5 => Some(Level::Trace),
92            _ => None,
93        }
94    }
95
96    #[must_use]
97    const fn min_usize() -> usize {
98        Self::most_severe() as usize
99    }
100
101    #[must_use]
102    const fn max_usize() -> usize {
103        Self::most_verbose() as usize
104    }
105
106    /// Returns the number of logging levels.
107    #[must_use]
108    pub const fn count() -> usize {
109        Self::max_usize() + 1
110    }
111
112    /// Returns the most severe logging level.
113    #[must_use]
114    pub const fn most_severe() -> Level {
115        Level::Critical
116    }
117
118    /// Returns the most verbose logging level.
119    #[must_use]
120    pub const fn most_verbose() -> Level {
121        Level::Trace
122    }
123
124    /// Returns the string representation.
125    ///
126    /// This returns the same string as the `fmt::Display` implementation.
127    #[must_use]
128    pub fn as_str(&self) -> &'static str {
129        LOG_LEVEL_NAMES[*self as usize]
130    }
131
132    #[must_use]
133    pub(crate) fn as_short_str(&self) -> &'static str {
134        LOG_LEVEL_SHORT_NAMES[*self as usize]
135    }
136
137    /// Iterates through all logging levels.
138    ///
139    /// The order of iteration is from more severe to more verbose.
140    ///
141    /// # Examples
142    ///
143    /// ```
144    /// use spdlog::Level;
145    ///
146    /// let mut levels = Level::iter();
147    ///
148    /// assert_eq!(Some(Level::Critical), levels.next());
149    /// assert_eq!(Some(Level::Trace), levels.last());
150    /// ```
151    pub fn iter() -> impl Iterator<Item = Self> {
152        (Self::min_usize()..=Self::max_usize()).map(|i| Self::from_usize(i).unwrap())
153    }
154}
155
156#[cfg(feature = "log")]
157impl From<log::Level> for Level {
158    fn from(level: log::Level) -> Self {
159        match level {
160            log::Level::Error => Self::Error,
161            log::Level::Warn => Self::Warn,
162            log::Level::Info => Self::Info,
163            log::Level::Debug => Self::Debug,
164            log::Level::Trace => Self::Trace,
165        }
166    }
167}
168
169impl fmt::Display for Level {
170    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171        f.write_str(self.as_str())
172    }
173}
174
175impl FromStr for Level {
176    type Err = Error;
177
178    fn from_str(level: &str) -> Result<Level, Self::Err> {
179        LOG_LEVEL_NAMES
180            .iter()
181            .position(|&name| name.eq_ignore_ascii_case(level))
182            .into_iter()
183            .map(|idx| Level::from_usize(idx).unwrap())
184            .next()
185            .ok_or_else(|| Error::ParseLevel(level.to_string()))
186    }
187}
188
189#[repr(u16)]
190#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, bytemuck::NoUninit)]
191enum LevelFilterDiscriminant {
192    Off,
193    Equal,
194    NotEqual,
195    MoreSevere,
196    MoreSevereEqual,
197    MoreVerbose,
198    MoreVerboseEqual,
199    All,
200}
201
202/// Represents log level logical filter conditions.
203///
204/// Use [`LevelFilter::test`] method to check if a [`Level`] satisfies the
205/// filter condition.
206#[repr(align(4))]
207#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
208pub enum LevelFilter {
209    /// Disables all levels.
210    Off,
211    /// Enables if the target level is equal to the filter level.
212    Equal(Level),
213    /// Enables if the target level is not equal to the filter level.
214    NotEqual(Level),
215    /// Enables if the target level is more severe than the filter level.
216    MoreSevere(Level),
217    /// Enables if the target level is more severe than or equal to the filter
218    /// level.
219    MoreSevereEqual(Level),
220    /// Enables if the target level is more verbose than the filter level.
221    MoreVerbose(Level),
222    /// Enables if the target level is more verbose than or equal to the filter
223    /// level.
224    MoreVerboseEqual(Level),
225    /// Enables all levels.
226    All,
227}
228
229impl LevelFilter {
230    /// Checks the given level if satisfies the filter condition.
231    #[deprecated(
232        since = "0.4.0",
233        note = "it may be removed in the future, use method `test()` instead"
234    )]
235    #[must_use]
236    pub fn compare(&self, level: Level) -> bool {
237        self.__test_const(level)
238    }
239
240    /// Checks the given level if satisfies the filter condition.
241    ///
242    /// # Examples
243    ///
244    /// ```
245    /// use spdlog::prelude::*;
246    ///
247    /// let level_filter = LevelFilter::MoreSevere(Level::Info);
248    ///
249    /// assert_eq!(level_filter.test(Level::Trace), false);
250    /// assert_eq!(level_filter.test(Level::Info), false);
251    /// assert_eq!(level_filter.test(Level::Warn), true);
252    /// assert_eq!(level_filter.test(Level::Error), true);
253    /// ```
254    #[must_use]
255    pub fn test(&self, level: Level) -> bool {
256        self.__test_const(level)
257    }
258
259    // Users should not use this function directly.
260    #[doc(hidden)]
261    #[must_use]
262    pub const fn __test_const(&self, level: Level) -> bool {
263        let level_num: u16 = level as u16;
264
265        match *self {
266            Self::Off => false,
267            Self::Equal(stored) => level_num == stored as u16,
268            Self::NotEqual(stored) => level_num != stored as u16,
269            Self::MoreSevere(stored) => level_num < stored as u16,
270            Self::MoreSevereEqual(stored) => level_num <= stored as u16,
271            Self::MoreVerbose(stored) => level_num > stored as u16,
272            Self::MoreVerboseEqual(stored) => level_num >= stored as u16,
273            Self::All => true,
274        }
275    }
276
277    #[must_use]
278    pub(crate) fn from_str_for_env(text: &str) -> Option<LevelFilter> {
279        if let Ok(level) = Level::from_str(text) {
280            Some(LevelFilter::MoreSevereEqual(level))
281        } else if text.eq_ignore_ascii_case("off") {
282            Some(LevelFilter::Off)
283        } else if text.eq_ignore_ascii_case("all") {
284            Some(LevelFilter::All)
285        } else {
286            None
287        }
288    }
289
290    #[must_use]
291    fn discriminant(&self) -> LevelFilterDiscriminant {
292        match self {
293            Self::Off => LevelFilterDiscriminant::Off,
294            Self::Equal(_) => LevelFilterDiscriminant::Equal,
295            Self::NotEqual(_) => LevelFilterDiscriminant::NotEqual,
296            Self::MoreSevere(_) => LevelFilterDiscriminant::MoreSevere,
297            Self::MoreSevereEqual(_) => LevelFilterDiscriminant::MoreSevereEqual,
298            Self::MoreVerbose(_) => LevelFilterDiscriminant::MoreVerbose,
299            Self::MoreVerboseEqual(_) => LevelFilterDiscriminant::MoreVerboseEqual,
300            Self::All => LevelFilterDiscriminant::All,
301        }
302    }
303
304    #[must_use]
305    fn level(&self) -> Option<Level> {
306        match *self {
307            Self::Equal(level)
308            | Self::NotEqual(level)
309            | Self::MoreSevere(level)
310            | Self::MoreSevereEqual(level)
311            | Self::MoreVerbose(level)
312            | Self::MoreVerboseEqual(level) => Some(level),
313            Self::Off | Self::All => None,
314        }
315    }
316}
317
318#[cfg(feature = "log")]
319impl From<log::LevelFilter> for LevelFilter {
320    fn from(filter: log::LevelFilter) -> Self {
321        match filter {
322            log::LevelFilter::Off => Self::Off,
323            filter => Self::MoreSevereEqual(Level::from(filter.to_level().unwrap())),
324        }
325    }
326}
327
328// Atomic
329
330// This is a version of `LevelFilter` that does not contain uninitialized bytes.
331// For variants like `LevelFilter::{Off,All}`, since they have no field, their
332// field memory space is treated as uninitialized bytes. `Atomic<T>` from the
333// `atomic` crate uses `std::mem::transmute`, and its operations on
334// uninitialized bytes are undefined behavior. Therefore, we created this
335// `LevelFilterLayout` type, where uninitialized bytes are filled with
336// `UNDEFINED_FALLBACK`, enabling safe atomic operations.
337#[repr(C, align(4))]
338#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, bytemuck::NoUninit)]
339struct LevelFilterLayout {
340    discriminant: LevelFilterDiscriminant,
341    level: Level,
342}
343
344impl LevelFilterLayout {
345    const UNDEFINED_FALLBACK: Level = Level::Critical;
346}
347
348impl From<LevelFilter> for LevelFilterLayout {
349    fn from(value: LevelFilter) -> Self {
350        Self {
351            discriminant: value.discriminant(),
352            level: value.level().unwrap_or(Self::UNDEFINED_FALLBACK),
353        }
354    }
355}
356
357impl From<LevelFilterLayout> for LevelFilter {
358    fn from(layout: LevelFilterLayout) -> Self {
359        match layout.discriminant {
360            LevelFilterDiscriminant::Off => Self::Off,
361            LevelFilterDiscriminant::Equal => Self::Equal(layout.level),
362            LevelFilterDiscriminant::NotEqual => Self::NotEqual(layout.level),
363            LevelFilterDiscriminant::MoreSevere => Self::MoreSevere(layout.level),
364            LevelFilterDiscriminant::MoreSevereEqual => Self::MoreSevereEqual(layout.level),
365            LevelFilterDiscriminant::MoreVerbose => Self::MoreVerbose(layout.level),
366            LevelFilterDiscriminant::MoreVerboseEqual => Self::MoreVerboseEqual(layout.level),
367            LevelFilterDiscriminant::All => Self::All,
368        }
369    }
370}
371
372/// Atomic version of [`LevelFilter`] which can be safely shared between
373/// threads.
374pub struct AtomicLevelFilter {
375    inner: atomic::Atomic<LevelFilterLayout>,
376}
377
378impl AtomicLevelFilter {
379    const ORDERING: Ordering = Ordering::Relaxed;
380
381    /// Creates a new `AtomicLevelFilter`.
382    pub fn new(init: LevelFilter) -> Self {
383        Self {
384            inner: atomic::Atomic::new(init.into()),
385        }
386    }
387
388    /// Loads the level filter with `Relaxed` ordering.
389    pub fn get(&self) -> LevelFilter {
390        self.load(Self::ORDERING)
391    }
392
393    /// Stores a level filter with `Relaxed` ordering.
394    pub fn set(&self, new: LevelFilter) {
395        self.store(new, Self::ORDERING);
396    }
397
398    /// Loads the level filter.
399    ///
400    /// # Panics
401    ///
402    /// Panics if the ordering is `Release` or `AcqRel`.
403    pub fn load(&self, ordering: Ordering) -> LevelFilter {
404        self.inner.load(ordering).into()
405    }
406
407    /// Stores a level filter.
408    ///
409    /// # Panics
410    ///
411    /// Panics if the ordering is `Acquire` or `AcqRel`.
412    pub fn store(&self, value: LevelFilter, ordering: Ordering) {
413        self.inner.store(value.into(), ordering);
414    }
415
416    /// Stores a level filter, returning the old level filter.
417    pub fn swap(&self, new: LevelFilter, ordering: Ordering) -> LevelFilter {
418        self.inner.swap(new.into(), ordering).into()
419    }
420}
421
422impl Debug for AtomicLevelFilter {
423    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
424        self.get().fmt(f)
425    }
426}
427
428#[cfg(test)]
429mod tests {
430    use std::mem::size_of;
431
432    use super::*;
433    use crate::utils::const_assert;
434
435    const_assert!(atomic::Atomic::<Level>::is_lock_free());
436    const_assert!(atomic::Atomic::<LevelFilter>::is_lock_free());
437    const_assert!(atomic::Atomic::<LevelFilterLayout>::is_lock_free());
438    const_assert!(size_of::<Level>() * 2 == size_of::<LevelFilter>());
439    const_assert!(align_of::<Level>() * 2 == align_of::<LevelFilter>());
440    const_assert!(size_of::<LevelFilter>() == size_of::<LevelFilterLayout>());
441    const_assert!(align_of::<LevelFilter>() == align_of::<LevelFilterLayout>());
442
443    #[test]
444    fn from_usize() {
445        assert_eq!(
446            Level::most_severe(),
447            Level::from_usize(Level::min_usize()).unwrap()
448        );
449
450        assert_eq!(
451            Level::most_verbose(),
452            Level::from_usize(Level::max_usize()).unwrap()
453        );
454    }
455
456    #[test]
457    fn from_str() {
458        fn to_random_case(input: &str) -> String {
459            input
460                .char_indices()
461                .map(|(i, char)| {
462                    if i % 2 != 0 {
463                        char.to_ascii_uppercase()
464                    } else {
465                        char.to_ascii_lowercase()
466                    }
467                })
468                .collect::<String>()
469        }
470
471        for (i, &name) in LOG_LEVEL_NAMES.iter().enumerate() {
472            let from_usize = Level::from_usize(i);
473            let from_str = (
474                Level::from_str(&name.to_lowercase()),
475                Level::from_str(&name.to_uppercase()),
476                Level::from_str(&to_random_case(name)),
477            );
478
479            assert_eq!(from_usize.unwrap(), from_str.0.unwrap());
480            assert_eq!(from_usize.unwrap(), from_str.1.unwrap());
481            assert_eq!(from_usize.unwrap(), from_str.2.unwrap());
482        }
483
484        assert!(Level::from_str("notexist").is_err());
485    }
486
487    #[test]
488    fn as_short_str() {
489        for (&name, &short_name) in LOG_LEVEL_NAMES.iter().zip(LOG_LEVEL_SHORT_NAMES.iter()) {
490            assert_eq!(
491                name.chars()
492                    .next()
493                    .unwrap()
494                    .to_ascii_uppercase()
495                    .to_string(),
496                short_name
497            );
498        }
499    }
500
501    #[test]
502    fn level_filter_from_str_for_env() {
503        assert_eq!(
504            LevelFilter::MoreSevereEqual(Level::Info),
505            LevelFilter::from_str_for_env("iNFo").unwrap()
506        );
507
508        assert_eq!(
509            LevelFilter::MoreSevereEqual(Level::Warn),
510            LevelFilter::from_str_for_env("wARn").unwrap()
511        );
512
513        assert_eq!(
514            LevelFilter::Off,
515            LevelFilter::from_str_for_env("oFf").unwrap()
516        );
517
518        assert_eq!(
519            LevelFilter::All,
520            LevelFilter::from_str_for_env("aLl").unwrap()
521        );
522    }
523
524    #[test]
525    fn iter() {
526        let mut iter = Level::iter();
527        assert_eq!(iter.next(), Some(Level::Critical));
528        assert_eq!(iter.next(), Some(Level::Error));
529        assert_eq!(iter.next(), Some(Level::Warn));
530        assert_eq!(iter.next(), Some(Level::Info));
531        assert_eq!(iter.next(), Some(Level::Debug));
532        assert_eq!(iter.next(), Some(Level::Trace));
533        assert_eq!(iter.next(), None);
534    }
535
536    #[test]
537    fn filter() {
538        assert!(!LevelFilter::Off.test(Level::Trace));
539        assert!(!LevelFilter::Off.test(Level::Critical));
540        assert!(!LevelFilter::Off.test(Level::Warn));
541
542        assert!(LevelFilter::Equal(Level::Error).test(Level::Error));
543        assert!(!LevelFilter::Equal(Level::Error).test(Level::Warn));
544        assert!(!LevelFilter::Equal(Level::Error).test(Level::Critical));
545
546        assert!(LevelFilter::NotEqual(Level::Error).test(Level::Trace));
547        assert!(LevelFilter::NotEqual(Level::Error).test(Level::Info));
548        assert!(!LevelFilter::NotEqual(Level::Error).test(Level::Error));
549
550        assert!(LevelFilter::MoreSevere(Level::Info).test(Level::Warn));
551        assert!(LevelFilter::MoreSevere(Level::Info).test(Level::Error));
552        assert!(!LevelFilter::MoreSevere(Level::Info).test(Level::Info));
553
554        assert!(LevelFilter::MoreSevereEqual(Level::Info).test(Level::Warn));
555        assert!(LevelFilter::MoreSevereEqual(Level::Info).test(Level::Info));
556        assert!(!LevelFilter::MoreSevereEqual(Level::Info).test(Level::Trace));
557
558        assert!(LevelFilter::MoreVerbose(Level::Error).test(Level::Warn));
559        assert!(LevelFilter::MoreVerbose(Level::Error).test(Level::Info));
560        assert!(!LevelFilter::MoreVerbose(Level::Error).test(Level::Error));
561
562        assert!(LevelFilter::MoreVerboseEqual(Level::Error).test(Level::Warn));
563        assert!(LevelFilter::MoreVerboseEqual(Level::Error).test(Level::Error));
564        assert!(!LevelFilter::MoreVerboseEqual(Level::Error).test(Level::Critical));
565
566        assert!(LevelFilter::All.test(Level::Trace));
567        assert!(LevelFilter::All.test(Level::Critical));
568        assert!(LevelFilter::All.test(Level::Error));
569    }
570
571    #[cfg(feature = "log")]
572    #[test]
573    fn filter_from_log() {
574        assert_eq!(LevelFilter::from(log::LevelFilter::Off), LevelFilter::Off);
575        assert_eq!(
576            LevelFilter::from(log::LevelFilter::Error),
577            LevelFilter::MoreSevereEqual(Level::Error)
578        );
579        assert_eq!(
580            LevelFilter::from(log::LevelFilter::Warn),
581            LevelFilter::MoreSevereEqual(Level::Warn)
582        );
583        assert_eq!(
584            LevelFilter::from(log::LevelFilter::Info),
585            LevelFilter::MoreSevereEqual(Level::Info)
586        );
587        assert_eq!(
588            LevelFilter::from(log::LevelFilter::Debug),
589            LevelFilter::MoreSevereEqual(Level::Debug)
590        );
591        assert_eq!(
592            LevelFilter::from(log::LevelFilter::Trace),
593            LevelFilter::MoreSevereEqual(Level::Trace)
594        );
595    }
596
597    #[test]
598    fn atomic_level_filter() {
599        let atomic_level_filter = AtomicLevelFilter::new(LevelFilter::All);
600
601        let assert_this = |new: LevelFilter| {
602            assert_ne!(atomic_level_filter.get(), new);
603            atomic_level_filter.set(new);
604            assert_eq!(atomic_level_filter.get(), new);
605        };
606
607        fn produce_all(cond: impl Fn(Level) -> LevelFilter) -> impl Iterator<Item = LevelFilter> {
608            Level::iter().map(cond)
609        }
610
611        assert_this(LevelFilter::Off);
612        produce_all(LevelFilter::Equal).for_each(assert_this);
613        produce_all(LevelFilter::NotEqual).for_each(assert_this);
614        produce_all(LevelFilter::MoreSevere).for_each(assert_this);
615        produce_all(LevelFilter::MoreSevereEqual).for_each(assert_this);
616        produce_all(LevelFilter::MoreVerbose).for_each(assert_this);
617        produce_all(LevelFilter::MoreVerboseEqual).for_each(assert_this);
618        assert_this(LevelFilter::All);
619    }
620}