Skip to main content

micromegas_tracing/
levels.rs

1//! Verbosity management
2use std::{
3    cmp, fmt,
4    hash::Hash,
5    str::FromStr,
6    sync::atomic::{self, AtomicU32},
7};
8
9/// An enum representing the verbosity levels for logging.
10#[repr(u32)]
11#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
12pub enum Level {
13    /// The "fatal" level.
14    ///
15    /// Crashes, panics.
16    Fatal = 1,
17    /// The "error" level.
18    ///
19    /// Designates very serious errors.
20    Error,
21    /// The "warn" level.
22    ///
23    /// Designates hazardous situations.
24    Warn,
25    /// The "info" level.
26    ///
27    /// Designates useful information.
28    Info,
29    /// The "debug" level.
30    ///
31    /// Designates lower priority information.
32    Debug,
33    /// The "trace" level.
34    ///
35    /// Designates very low priority, often extremely verbose, information.
36    Trace,
37}
38
39impl Level {
40    pub fn from_value(value: u32) -> Option<Self> {
41        match value {
42            1 => Some(Self::Fatal),
43            2 => Some(Self::Error),
44            3 => Some(Self::Warn),
45            4 => Some(Self::Info),
46            5 => Some(Self::Debug),
47            6 => Some(Self::Trace),
48            _ => None,
49        }
50    }
51
52    pub fn parse(value: &str) -> Option<Self> {
53        match value {
54            "fatal" => Some(Self::Fatal),
55            "error" => Some(Self::Error),
56            "warning" => Some(Self::Warn),
57            "info" => Some(Self::Info),
58            "debug" => Some(Self::Debug),
59            "trace" => Some(Self::Trace),
60            _ => None,
61        }
62    }
63}
64
65/// An enum representing the available verbosity level filters of the logger.
66#[repr(u32)]
67#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
68pub enum LevelFilter {
69    /// A level lower than all log levels.
70    Off,
71    /// Corresponds to the `Fatal` log level.
72    Fatal,
73    /// Corresponds to the `Error` log level.
74    Error,
75    /// Corresponds to the `Warn` log level.
76    Warn,
77    /// Corresponds to the `Info` log level.
78    Info,
79    /// Corresponds to the `Debug` log level.
80    Debug,
81    /// Corresponds to the `Trace` log level.
82    Trace,
83}
84
85impl PartialEq<LevelFilter> for Level {
86    #[inline(always)]
87    fn eq(&self, other: &LevelFilter) -> bool {
88        *self as u32 == *other as u32
89    }
90}
91
92impl PartialOrd for Level {
93    #[inline(always)]
94    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
95        Some(self.cmp(other))
96    }
97
98    #[inline(always)]
99    fn lt(&self, other: &Self) -> bool {
100        (*self as u32) < *other as u32
101    }
102
103    #[inline(always)]
104    fn le(&self, other: &Self) -> bool {
105        *self as u32 <= *other as u32
106    }
107
108    #[inline(always)]
109    fn gt(&self, other: &Self) -> bool {
110        *self as u32 > *other as u32
111    }
112
113    #[inline(always)]
114    fn ge(&self, other: &Self) -> bool {
115        *self as u32 >= *other as u32
116    }
117}
118
119impl PartialOrd<LevelFilter> for Level {
120    #[inline(always)]
121    fn partial_cmp(&self, other: &LevelFilter) -> Option<cmp::Ordering> {
122        Some((*self as u32).cmp(&(*other as u32)))
123    }
124
125    #[inline(always)]
126    fn lt(&self, other: &LevelFilter) -> bool {
127        (*self as u32) < *other as u32
128    }
129
130    #[inline(always)]
131    fn le(&self, other: &LevelFilter) -> bool {
132        *self as u32 <= *other as u32
133    }
134
135    #[inline(always)]
136    fn gt(&self, other: &LevelFilter) -> bool {
137        *self as u32 > *other as u32
138    }
139
140    #[inline(always)]
141    fn ge(&self, other: &LevelFilter) -> bool {
142        *self as u32 >= *other as u32
143    }
144}
145
146impl Ord for Level {
147    #[inline(always)]
148    fn cmp(&self, other: &Self) -> cmp::Ordering {
149        (*self as u32).cmp(&(*other as u32))
150    }
151}
152
153fn ok_or<T, E>(t: Option<T>, e: E) -> Result<T, E> {
154    match t {
155        Some(t) => Ok(t),
156        None => Err(e),
157    }
158}
159
160pub struct ParseLevelError(());
161
162impl FromStr for Level {
163    type Err = ParseLevelError;
164    fn from_str(level: &str) -> Result<Self, Self::Err> {
165        ok_or(
166            LEVEL_NAMES
167                .iter()
168                .position(|&name| str::eq_ignore_ascii_case(name, level))
169                .into_iter()
170                .filter(|&idx| idx != 0)
171                .map(|idx| Self::from_u32(idx as u32).unwrap())
172                .next(),
173            ParseLevelError(()),
174        )
175    }
176}
177
178impl fmt::Display for Level {
179    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
180        fmt.pad(self.as_str())
181    }
182}
183
184impl Level {
185    pub(crate) fn from_u32(u: u32) -> Option<Self> {
186        match u {
187            1 => Some(Self::Fatal),
188            2 => Some(Self::Error),
189            3 => Some(Self::Warn),
190            4 => Some(Self::Info),
191            5 => Some(Self::Debug),
192            6 => Some(Self::Trace),
193            _ => None,
194        }
195    }
196    /// Returns the most verbose logging level.
197    #[inline(always)]
198    pub fn max() -> Self {
199        Self::Trace
200    }
201
202    /// Converts the `Level` to the equivalent `LevelFilter`.
203    #[inline(always)]
204    pub fn to_level_filter(self) -> LevelFilter {
205        LevelFilter::from_u32(self as u32).unwrap()
206    }
207
208    /// Returns the string representation of the `Level`.
209    ///
210    /// This returns the same string as the `fmt::Display` implementation.
211    pub fn as_str(self) -> &'static str {
212        LEVEL_NAMES[self as usize]
213    }
214
215    /// Iterate through all supported logging levels.
216    ///
217    /// The order of iteration is from more severe to less severe log messages.
218    ///
219    /// # Examples
220    ///
221    /// ```
222    /// use micromegas_tracing::prelude::*;
223    ///
224    /// let mut levels = Level::iter();
225    ///
226    /// assert_eq!(Some(Level::Fatal), levels.next());
227    /// assert_eq!(Some(Level::Trace), levels.last());
228    /// ```
229    pub fn iter() -> impl Iterator<Item = Self> {
230        (1..7).map(|i| Self::from_u32(i).unwrap())
231    }
232}
233
234impl PartialEq<Level> for LevelFilter {
235    #[inline(always)]
236    fn eq(&self, other: &Level) -> bool {
237        other.eq(self)
238    }
239}
240
241impl PartialOrd for LevelFilter {
242    #[inline(always)]
243    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
244        Some(self.cmp(other))
245    }
246
247    #[inline(always)]
248    fn lt(&self, other: &Self) -> bool {
249        (*self as u32) < *other as u32
250    }
251
252    #[inline(always)]
253    fn le(&self, other: &Self) -> bool {
254        *self as u32 <= *other as u32
255    }
256
257    #[inline(always)]
258    fn gt(&self, other: &Self) -> bool {
259        *self as u32 > *other as u32
260    }
261
262    #[inline(always)]
263    fn ge(&self, other: &Self) -> bool {
264        *self as u32 >= *other as u32
265    }
266}
267
268impl PartialOrd<Level> for LevelFilter {
269    #[inline(always)]
270    fn partial_cmp(&self, other: &Level) -> Option<cmp::Ordering> {
271        Some((*self as u32).cmp(&(*other as u32)))
272    }
273
274    #[inline(always)]
275    fn lt(&self, other: &Level) -> bool {
276        (*self as u32) < *other as u32
277    }
278
279    #[inline(always)]
280    fn le(&self, other: &Level) -> bool {
281        *self as u32 <= *other as u32
282    }
283
284    #[inline(always)]
285    fn gt(&self, other: &Level) -> bool {
286        *self as u32 > *other as u32
287    }
288
289    #[inline(always)]
290    fn ge(&self, other: &Level) -> bool {
291        *self as u32 >= *other as u32
292    }
293}
294
295impl Ord for LevelFilter {
296    #[inline(always)]
297    fn cmp(&self, other: &Self) -> cmp::Ordering {
298        (*self as u32).cmp(&(*other as u32))
299    }
300}
301
302impl FromStr for LevelFilter {
303    type Err = ParseLevelError;
304    fn from_str(level: &str) -> Result<Self, Self::Err> {
305        ok_or(
306            LEVEL_NAMES
307                .iter()
308                .position(|&name| str::eq_ignore_ascii_case(name, level))
309                .map(|p| Self::from_u32(p as u32).unwrap()),
310            ParseLevelError(()),
311        )
312    }
313}
314
315impl fmt::Display for LevelFilter {
316    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
317        fmt.pad(self.as_str())
318    }
319}
320
321impl LevelFilter {
322    pub(crate) fn from_u32(u: u32) -> Option<Self> {
323        match u {
324            0 => Some(Self::Off),
325            1 => Some(Self::Fatal),
326            2 => Some(Self::Error),
327            3 => Some(Self::Warn),
328            4 => Some(Self::Info),
329            5 => Some(Self::Debug),
330            6 => Some(Self::Trace),
331            _ => None,
332        }
333    }
334
335    /// Returns the most verbose logging level filter.
336    #[inline(always)]
337    pub fn max() -> Self {
338        Self::Trace
339    }
340
341    /// Converts `self` to the equivalent `Level`.
342    ///
343    /// Returns `None` if `self` is `LevelFilter::Off`.
344    #[inline(always)]
345    pub fn to_level(self) -> Option<Level> {
346        Level::from_u32(self as u32)
347    }
348
349    /// Returns the string representation of the `LevelFilter`.
350    ///
351    /// This returns the same string as the `fmt::Display` implementation.
352    pub fn as_str(self) -> &'static str {
353        LEVEL_NAMES[self as usize]
354    }
355
356    /// Iterate through all supported filtering levels.
357    ///
358    /// The order of iteration is from less to more verbose filtering.
359    ///
360    /// # Examples
361    ///
362    /// ```
363    /// use micromegas_tracing::prelude::*;
364    ///
365    /// let mut levels = LevelFilter::iter();
366    ///
367    /// assert_eq!(Some(LevelFilter::Off), levels.next());
368    /// assert_eq!(Some(LevelFilter::Trace), levels.last());
369    /// ```
370    pub fn iter() -> impl Iterator<Item = Self> {
371        (0..7).map(|i| Self::from_u32(i).unwrap())
372    }
373}
374
375#[repr(u32)]
376#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
377/// An enum representing the level of verbosity for `metrics`/`thread_spans`/`spans`.
378pub enum Verbosity {
379    /// The "min" level.
380    ///
381    /// Designates vey low details events, meaning overall lower frequency.
382    Min = 1,
383    /// The "med" level.
384    ///
385    /// Designates medium level level of details, meaning overall medium frequency
386    Med,
387    /// The "Max" level.
388    ///
389    /// Designates very high frequency events.
390    Max,
391}
392
393/// An enum representing the available verbosity level filters of the logger.
394#[repr(u32)]
395#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
396pub enum LodFilter {
397    /// A level lower than all log levels.
398    Off,
399    /// Corresponds to the `Min` log level.
400    Min,
401    /// Corresponds to the `Med` log level.
402    Med,
403    /// Corresponds to the `Max` log level.
404    Max,
405}
406
407impl PartialEq<LodFilter> for Verbosity {
408    #[inline(always)]
409    fn eq(&self, other: &LodFilter) -> bool {
410        *self as u32 == *other as u32
411    }
412}
413
414impl PartialOrd for Verbosity {
415    #[inline(always)]
416    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
417        Some(self.cmp(other))
418    }
419
420    #[inline(always)]
421    fn lt(&self, other: &Self) -> bool {
422        (*self as u32) < *other as u32
423    }
424
425    #[inline(always)]
426    fn le(&self, other: &Self) -> bool {
427        *self as u32 <= *other as u32
428    }
429
430    #[inline(always)]
431    fn gt(&self, other: &Self) -> bool {
432        *self as u32 > *other as u32
433    }
434
435    #[inline(always)]
436    fn ge(&self, other: &Self) -> bool {
437        *self as u32 >= *other as u32
438    }
439}
440
441impl PartialOrd<LodFilter> for Verbosity {
442    #[inline(always)]
443    fn partial_cmp(&self, other: &LodFilter) -> Option<cmp::Ordering> {
444        Some((*self as u32).cmp(&(*other as u32)))
445    }
446
447    #[inline(always)]
448    fn lt(&self, other: &LodFilter) -> bool {
449        (*self as u32) < *other as u32
450    }
451
452    #[inline(always)]
453    fn le(&self, other: &LodFilter) -> bool {
454        *self as u32 <= *other as u32
455    }
456
457    #[inline(always)]
458    fn gt(&self, other: &LodFilter) -> bool {
459        *self as u32 > *other as u32
460    }
461
462    #[inline(always)]
463    fn ge(&self, other: &LodFilter) -> bool {
464        *self as u32 >= *other as u32
465    }
466}
467
468impl Ord for Verbosity {
469    #[inline(always)]
470    fn cmp(&self, other: &Self) -> cmp::Ordering {
471        (*self as u32).cmp(&(*other as u32))
472    }
473}
474
475impl FromStr for Verbosity {
476    type Err = ParseLevelError;
477    fn from_str(level: &str) -> Result<Self, Self::Err> {
478        ok_or(
479            LOD_NAMES
480                .iter()
481                .position(|&name| str::eq_ignore_ascii_case(name, level))
482                .into_iter()
483                .filter(|&idx| idx != 0)
484                .map(|idx| Self::from_usize(idx).unwrap())
485                .next(),
486            ParseLevelError(()),
487        )
488    }
489}
490
491impl fmt::Display for Verbosity {
492    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
493        fmt.pad(self.as_str())
494    }
495}
496
497impl Verbosity {
498    fn from_usize(u: usize) -> Option<Self> {
499        match u {
500            1 => Some(Self::Min),
501            2 => Some(Self::Med),
502            3 => Some(Self::Max),
503            _ => None,
504        }
505    }
506
507    fn from_u32(u: u32) -> Option<Self> {
508        match u {
509            1 => Some(Self::Min),
510            2 => Some(Self::Med),
511            3 => Some(Self::Max),
512            _ => None,
513        }
514    }
515
516    /// Returns the most verbose logging level.
517    #[inline(always)]
518    pub fn max() -> Self {
519        Self::Max
520    }
521
522    /// Converts the `Lod` to the equivalent `LodFilter`.
523    #[inline(always)]
524    pub fn to_level_filter(self) -> LodFilter {
525        LodFilter::from_u32(self as u32).unwrap()
526    }
527
528    /// Returns the string representation of the `Lod`.
529    ///
530    /// This returns the same string as the `fmt::Display` implementation.
531    pub fn as_str(self) -> &'static str {
532        LOD_NAMES[self as usize]
533    }
534
535    /// Iterate through all supported logging levels.
536    ///
537    /// The order of iteration is from more severe to less severe log messages.
538    ///
539    /// # Examples
540    ///
541    /// ```
542    /// use micromegas_tracing::prelude::*;
543    ///
544    /// let mut lods = Verbosity::iter();
545    ///
546    /// assert_eq!(Some(Verbosity::Min), lods.next());
547    /// assert_eq!(Some(Verbosity::Max), lods.last());
548    /// ```
549    pub fn iter() -> impl Iterator<Item = Self> {
550        (1..4).map(|i| Self::from_usize(i).unwrap())
551    }
552}
553
554impl PartialEq<Verbosity> for LodFilter {
555    #[inline(always)]
556    fn eq(&self, other: &Verbosity) -> bool {
557        other.eq(self)
558    }
559}
560
561impl PartialOrd for LodFilter {
562    #[inline(always)]
563    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
564        Some(self.cmp(other))
565    }
566
567    #[inline(always)]
568    fn lt(&self, other: &Self) -> bool {
569        (*self as u32) < *other as u32
570    }
571
572    #[inline(always)]
573    fn le(&self, other: &Self) -> bool {
574        *self as u32 <= *other as u32
575    }
576
577    #[inline(always)]
578    fn gt(&self, other: &Self) -> bool {
579        *self as u32 > *other as u32
580    }
581
582    #[inline(always)]
583    fn ge(&self, other: &Self) -> bool {
584        *self as u32 >= *other as u32
585    }
586}
587
588impl PartialOrd<Verbosity> for LodFilter {
589    #[inline(always)]
590    fn partial_cmp(&self, other: &Verbosity) -> Option<cmp::Ordering> {
591        Some((*self as u32).cmp(&(*other as u32)))
592    }
593
594    #[inline(always)]
595    fn lt(&self, other: &Verbosity) -> bool {
596        (*self as u32) < *other as u32
597    }
598
599    #[inline(always)]
600    fn le(&self, other: &Verbosity) -> bool {
601        *self as u32 <= *other as u32
602    }
603
604    #[inline(always)]
605    fn gt(&self, other: &Verbosity) -> bool {
606        *self as u32 > *other as u32
607    }
608
609    #[inline(always)]
610    fn ge(&self, other: &Verbosity) -> bool {
611        *self as u32 >= *other as u32
612    }
613}
614
615impl Ord for LodFilter {
616    #[inline(always)]
617    fn cmp(&self, other: &Self) -> cmp::Ordering {
618        (*self as u32).cmp(&(*other as u32))
619    }
620}
621
622impl FromStr for LodFilter {
623    type Err = ParseLevelError;
624    fn from_str(level: &str) -> Result<Self, Self::Err> {
625        ok_or(
626            LOD_NAMES
627                .iter()
628                .position(|&name| str::eq_ignore_ascii_case(name, level))
629                .map(|p| Self::from_usize(p).unwrap()),
630            ParseLevelError(()),
631        )
632    }
633}
634
635impl fmt::Display for LodFilter {
636    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
637        fmt.pad(self.as_str())
638    }
639}
640
641impl LodFilter {
642    fn from_usize(u: usize) -> Option<Self> {
643        match u {
644            0 => Some(Self::Off),
645            1 => Some(Self::Min),
646            2 => Some(Self::Med),
647            3 => Some(Self::Max),
648            _ => None,
649        }
650    }
651
652    fn from_u32(u: u32) -> Option<Self> {
653        match u {
654            0 => Some(Self::Off),
655            1 => Some(Self::Min),
656            2 => Some(Self::Med),
657            3 => Some(Self::Max),
658            _ => None,
659        }
660    }
661
662    /// Returns the most verbose logging level filter.
663    #[inline(always)]
664    pub fn max() -> Self {
665        Self::Max
666    }
667
668    /// Converts `self` to the equivalent `Lod`.
669    ///
670    /// Returns `None` if `self` is `LodFilter::Off`.
671    #[inline(always)]
672    pub fn to_level(self) -> Option<Verbosity> {
673        Verbosity::from_u32(self as u32)
674    }
675
676    /// Returns the string representation of the `LodFilter`.
677    ///
678    /// This returns the same string as the `fmt::Display` implementation.
679    pub fn as_str(self) -> &'static str {
680        LOD_NAMES[self as usize]
681    }
682
683    /// Iterate through all supported filtering levels.
684    ///
685    /// The order of iteration is from less to more verbose filtering.
686    ///
687    /// # Examples
688    ///
689    /// ```
690    /// use micromegas_tracing::prelude::*;
691    ///
692    /// let mut lod_filters = LodFilter::iter();
693    ///
694    /// assert_eq!(Some(LodFilter::Off), lod_filters.next());
695    /// assert_eq!(Some(LodFilter::Max), lod_filters.last());
696    /// ```
697    pub fn iter() -> impl Iterator<Item = Self> {
698        (0..4).map(|i| Self::from_usize(i).unwrap())
699    }
700}
701
702static MAX_LEVEL_FILTER: AtomicU32 = AtomicU32::new(0);
703static MAX_LOD_FILTER: AtomicU32 = AtomicU32::new(0);
704
705static LEVEL_NAMES: [&str; 7] = ["OFF", "FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"];
706static LOD_NAMES: [&str; 4] = ["OFF", "LOW", "MED", "HIGH"];
707
708/// Sets the global maximum log level.
709///
710/// Generally, this should only be called by the active logging implementation.
711///
712/// Note that `Trace` is the maximum level, because it provides the maximum amount of detail in the emitted logs.
713#[inline(always)]
714pub fn set_max_level(level: LevelFilter) {
715    MAX_LEVEL_FILTER.store(level as u32, atomic::Ordering::Relaxed);
716}
717
718/// Returns the current maximum log level.
719#[inline(always)]
720pub fn max_level() -> LevelFilter {
721    // Since `LevelFilter` is `repr(u32)`,
722    // this transmute is sound if and only if `MAX_LOG_LEVEL_FILTER`
723    // is set to a u32 that is a valid discriminant for `LevelFilter`.
724    // Since `MAX_LOG_LEVEL_FILTER` is private, the only time it's set
725    // is by `set_max_level` above, i.e. by casting a `LevelFilter` to `u32`.
726    // So any u32 stored in `MAX_LOG_LEVEL_FILTER` is a valid discriminant.
727    unsafe { std::mem::transmute(MAX_LEVEL_FILTER.load(atomic::Ordering::Relaxed)) }
728}
729
730/// Sets the global maximum log level.
731#[inline(always)]
732pub fn set_max_lod(level: LodFilter) {
733    MAX_LOD_FILTER.store(level as u32, atomic::Ordering::Relaxed);
734}
735
736/// Returns the current maximum log level.
737#[inline(always)]
738pub fn max_lod() -> LodFilter {
739    // See comment above
740    unsafe { std::mem::transmute(MAX_LOD_FILTER.load(atomic::Ordering::Relaxed)) }
741}
742
743/// The statically resolved maximum log level.
744pub const STATIC_MAX_LEVEL: LevelFilter = LevelFilter::Trace;
745
746/// The statically resolved maximum metrics/spans lod.
747pub const STATIC_MAX_LOD: LodFilter = LodFilter::Max;