Skip to main content

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    #[must_use]
383    pub fn new(init: LevelFilter) -> Self {
384        Self {
385            inner: atomic::Atomic::new(init.into()),
386        }
387    }
388
389    /// Loads the level filter with `Relaxed` ordering.
390    pub fn get(&self) -> LevelFilter {
391        self.load(Self::ORDERING)
392    }
393
394    /// Stores a level filter with `Relaxed` ordering.
395    pub fn set(&self, new: LevelFilter) {
396        self.store(new, Self::ORDERING);
397    }
398
399    /// Loads the level filter.
400    ///
401    /// # Panics
402    ///
403    /// Panics if the ordering is `Release` or `AcqRel`.
404    pub fn load(&self, ordering: Ordering) -> LevelFilter {
405        self.inner.load(ordering).into()
406    }
407
408    /// Stores a level filter.
409    ///
410    /// # Panics
411    ///
412    /// Panics if the ordering is `Acquire` or `AcqRel`.
413    pub fn store(&self, value: LevelFilter, ordering: Ordering) {
414        self.inner.store(value.into(), ordering);
415    }
416
417    /// Stores a level filter, returning the old level filter.
418    pub fn swap(&self, new: LevelFilter, ordering: Ordering) -> LevelFilter {
419        self.inner.swap(new.into(), ordering).into()
420    }
421}
422
423impl Debug for AtomicLevelFilter {
424    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
425        self.get().fmt(f)
426    }
427}
428
429#[cfg(test)]
430mod tests {
431    use std::mem::size_of;
432
433    use super::*;
434    use crate::utils::const_assert;
435
436    const_assert!(atomic::Atomic::<Level>::is_lock_free());
437    const_assert!(atomic::Atomic::<LevelFilter>::is_lock_free());
438    const_assert!(atomic::Atomic::<LevelFilterLayout>::is_lock_free());
439    const_assert!(size_of::<Level>() * 2 == size_of::<LevelFilter>());
440    const_assert!(align_of::<Level>() * 2 == align_of::<LevelFilter>());
441    const_assert!(size_of::<LevelFilter>() == size_of::<LevelFilterLayout>());
442    const_assert!(align_of::<LevelFilter>() == align_of::<LevelFilterLayout>());
443
444    #[test]
445    fn from_usize() {
446        assert_eq!(
447            Level::most_severe(),
448            Level::from_usize(Level::min_usize()).unwrap()
449        );
450
451        assert_eq!(
452            Level::most_verbose(),
453            Level::from_usize(Level::max_usize()).unwrap()
454        );
455    }
456
457    #[test]
458    fn from_str() {
459        fn to_random_case(input: &str) -> String {
460            input
461                .char_indices()
462                .map(|(i, char)| {
463                    if i % 2 != 0 {
464                        char.to_ascii_uppercase()
465                    } else {
466                        char.to_ascii_lowercase()
467                    }
468                })
469                .collect::<String>()
470        }
471
472        for (i, &name) in LOG_LEVEL_NAMES.iter().enumerate() {
473            let from_usize = Level::from_usize(i);
474            let from_str = (
475                Level::from_str(&name.to_lowercase()),
476                Level::from_str(&name.to_uppercase()),
477                Level::from_str(&to_random_case(name)),
478            );
479
480            assert_eq!(from_usize.unwrap(), from_str.0.unwrap());
481            assert_eq!(from_usize.unwrap(), from_str.1.unwrap());
482            assert_eq!(from_usize.unwrap(), from_str.2.unwrap());
483        }
484
485        assert!(Level::from_str("notexist").is_err());
486    }
487
488    #[test]
489    fn as_short_str() {
490        for (&name, &short_name) in LOG_LEVEL_NAMES.iter().zip(LOG_LEVEL_SHORT_NAMES.iter()) {
491            assert_eq!(
492                name.chars()
493                    .next()
494                    .unwrap()
495                    .to_ascii_uppercase()
496                    .to_string(),
497                short_name
498            );
499        }
500    }
501
502    #[test]
503    fn level_filter_from_str_for_env() {
504        assert_eq!(
505            LevelFilter::MoreSevereEqual(Level::Info),
506            LevelFilter::from_str_for_env("iNFo").unwrap()
507        );
508
509        assert_eq!(
510            LevelFilter::MoreSevereEqual(Level::Warn),
511            LevelFilter::from_str_for_env("wARn").unwrap()
512        );
513
514        assert_eq!(
515            LevelFilter::Off,
516            LevelFilter::from_str_for_env("oFf").unwrap()
517        );
518
519        assert_eq!(
520            LevelFilter::All,
521            LevelFilter::from_str_for_env("aLl").unwrap()
522        );
523    }
524
525    #[test]
526    fn iter() {
527        let mut iter = Level::iter();
528        assert_eq!(iter.next(), Some(Level::Critical));
529        assert_eq!(iter.next(), Some(Level::Error));
530        assert_eq!(iter.next(), Some(Level::Warn));
531        assert_eq!(iter.next(), Some(Level::Info));
532        assert_eq!(iter.next(), Some(Level::Debug));
533        assert_eq!(iter.next(), Some(Level::Trace));
534        assert_eq!(iter.next(), None);
535    }
536
537    #[test]
538    fn filter() {
539        assert!(!LevelFilter::Off.test(Level::Trace));
540        assert!(!LevelFilter::Off.test(Level::Critical));
541        assert!(!LevelFilter::Off.test(Level::Warn));
542
543        assert!(LevelFilter::Equal(Level::Error).test(Level::Error));
544        assert!(!LevelFilter::Equal(Level::Error).test(Level::Warn));
545        assert!(!LevelFilter::Equal(Level::Error).test(Level::Critical));
546
547        assert!(LevelFilter::NotEqual(Level::Error).test(Level::Trace));
548        assert!(LevelFilter::NotEqual(Level::Error).test(Level::Info));
549        assert!(!LevelFilter::NotEqual(Level::Error).test(Level::Error));
550
551        assert!(LevelFilter::MoreSevere(Level::Info).test(Level::Warn));
552        assert!(LevelFilter::MoreSevere(Level::Info).test(Level::Error));
553        assert!(!LevelFilter::MoreSevere(Level::Info).test(Level::Info));
554
555        assert!(LevelFilter::MoreSevereEqual(Level::Info).test(Level::Warn));
556        assert!(LevelFilter::MoreSevereEqual(Level::Info).test(Level::Info));
557        assert!(!LevelFilter::MoreSevereEqual(Level::Info).test(Level::Trace));
558
559        assert!(LevelFilter::MoreVerbose(Level::Error).test(Level::Warn));
560        assert!(LevelFilter::MoreVerbose(Level::Error).test(Level::Info));
561        assert!(!LevelFilter::MoreVerbose(Level::Error).test(Level::Error));
562
563        assert!(LevelFilter::MoreVerboseEqual(Level::Error).test(Level::Warn));
564        assert!(LevelFilter::MoreVerboseEqual(Level::Error).test(Level::Error));
565        assert!(!LevelFilter::MoreVerboseEqual(Level::Error).test(Level::Critical));
566
567        assert!(LevelFilter::All.test(Level::Trace));
568        assert!(LevelFilter::All.test(Level::Critical));
569        assert!(LevelFilter::All.test(Level::Error));
570    }
571
572    #[cfg(feature = "log")]
573    #[test]
574    fn filter_from_log() {
575        assert_eq!(LevelFilter::from(log::LevelFilter::Off), LevelFilter::Off);
576        assert_eq!(
577            LevelFilter::from(log::LevelFilter::Error),
578            LevelFilter::MoreSevereEqual(Level::Error)
579        );
580        assert_eq!(
581            LevelFilter::from(log::LevelFilter::Warn),
582            LevelFilter::MoreSevereEqual(Level::Warn)
583        );
584        assert_eq!(
585            LevelFilter::from(log::LevelFilter::Info),
586            LevelFilter::MoreSevereEqual(Level::Info)
587        );
588        assert_eq!(
589            LevelFilter::from(log::LevelFilter::Debug),
590            LevelFilter::MoreSevereEqual(Level::Debug)
591        );
592        assert_eq!(
593            LevelFilter::from(log::LevelFilter::Trace),
594            LevelFilter::MoreSevereEqual(Level::Trace)
595        );
596    }
597
598    #[test]
599    fn atomic_level_filter() {
600        let atomic_level_filter = AtomicLevelFilter::new(LevelFilter::All);
601
602        let assert_this = |new: LevelFilter| {
603            assert_ne!(atomic_level_filter.get(), new);
604            atomic_level_filter.set(new);
605            assert_eq!(atomic_level_filter.get(), new);
606        };
607
608        fn produce_all(cond: impl Fn(Level) -> LevelFilter) -> impl Iterator<Item = LevelFilter> {
609            Level::iter().map(cond)
610        }
611
612        assert_this(LevelFilter::Off);
613        produce_all(LevelFilter::Equal).for_each(assert_this);
614        produce_all(LevelFilter::NotEqual).for_each(assert_this);
615        produce_all(LevelFilter::MoreSevere).for_each(assert_this);
616        produce_all(LevelFilter::MoreSevereEqual).for_each(assert_this);
617        produce_all(LevelFilter::MoreVerbose).for_each(assert_this);
618        produce_all(LevelFilter::MoreVerboseEqual).for_each(assert_this);
619        assert_this(LevelFilter::All);
620    }
621
622    #[test]
623    fn level_count() {
624        assert_eq!(Level::iter().count(), Level::count());
625    }
626}