spdlog/
level.rs

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