micromegas_tracing/logs/
events.rs

1use crate::levels::{Level, LevelFilter};
2use std::sync::atomic::{AtomicU32, Ordering};
3
4#[derive(Debug)]
5pub struct LogMetadata<'a> {
6    pub level: Level,
7    pub level_filter: AtomicU32,
8    pub fmt_str: &'a str,
9    pub target: &'a str,
10    pub module_path: &'a str,
11    pub file: &'a str,
12    pub line: u32,
13}
14
15pub const FILTER_LEVEL_UNSET_VALUE: u32 = 0xF;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum FilterState {
19    /// The filter needs to be updated.
20    Outdated,
21    /// The filter is up to date but no filter is set.
22    NotSet,
23    /// The filter is up to date a filter level is set.
24    Set(LevelFilter),
25}
26
27impl LogMetadata<'_> {
28    /// This is a way to efficiency implement finer grade filtering by amortizing its
29    /// cost. An atomic is used to store a level filter and a 16 bit generation.
30    /// Allowing a config update to be applied to the level filter multiple times during
31    /// the lifetime of the process.
32    ///
33    /// ```ignore
34    /// const GENERATION: u16 = 1;
35    /// let level_filter = metadata.level_filter(GENERATION).unwrap_or_else(|| {
36    ///     let level_filter = self.level_filter(metadata.target);
37    ///     metadata.set_level_filter(level_filter, GENERATION);
38    ///     level_filter
39    /// });
40    /// if metadata.level <= level_filter {
41    ///     ...
42    /// }
43    /// ```
44    ///
45    pub fn level_filter(&self, generation: u16) -> FilterState {
46        let level_filter = self.level_filter.load(Ordering::Relaxed);
47        if generation > ((level_filter >> 16) as u16) {
48            FilterState::Outdated
49        } else {
50            LevelFilter::from_u32(level_filter & FILTER_LEVEL_UNSET_VALUE)
51                .map_or(FilterState::NotSet, |level_filter| {
52                    FilterState::Set(level_filter)
53                })
54        }
55    }
56
57    /// Sets the level filter if the generation is greater than the current generation.
58    ///
59    pub fn set_level_filter(&self, generation: u16, level_filter: Option<LevelFilter>) {
60        let new = level_filter.map_or(FILTER_LEVEL_UNSET_VALUE, |filter_level| filter_level as u32)
61            | (u32::from(generation) << 16);
62        let mut current = self.level_filter.load(Ordering::Relaxed);
63        if generation <= (current >> 16) as u16 {
64            // value was updated form another thread with a newer generation
65            return;
66        }
67        loop {
68            match self.level_filter.compare_exchange(
69                current,
70                new,
71                Ordering::SeqCst,
72                Ordering::Relaxed,
73            ) {
74                Ok(_) => {
75                    return;
76                }
77                Err(x) => {
78                    if generation <= (x >> 16) as u16 {
79                        return;
80                    }
81                    current = x;
82                }
83            };
84        }
85    }
86}
87
88#[allow(unused_imports)]
89#[cfg(test)]
90mod test {
91    use crate::logs::{
92        FILTER_LEVEL_UNSET_VALUE, FilterState, LogMetadata,
93        events::{Level, LevelFilter},
94    };
95    use std::thread;
96
97    #[test]
98    fn test_filter_levels() {
99        static METADATA: LogMetadata = LogMetadata {
100            level: Level::Trace,
101            level_filter: std::sync::atomic::AtomicU32::new(FILTER_LEVEL_UNSET_VALUE),
102            fmt_str: "$crate::__first_arg!($($arg)+)",
103            target: module_path!(),
104            module_path: module_path!(),
105            file: file!(),
106            line: line!(),
107        };
108        assert_eq!(METADATA.level_filter(1), FilterState::Outdated);
109        METADATA.set_level_filter(1, Some(LevelFilter::Trace));
110        assert_eq!(
111            METADATA.level_filter(1),
112            FilterState::Set(LevelFilter::Trace)
113        );
114        METADATA.set_level_filter(1, Some(LevelFilter::Debug));
115        assert_eq!(
116            METADATA.level_filter(1),
117            FilterState::Set(LevelFilter::Trace)
118        );
119        METADATA.set_level_filter(1, None);
120        assert_eq!(
121            METADATA.level_filter(1),
122            FilterState::Set(LevelFilter::Trace)
123        );
124        METADATA.set_level_filter(2, Some(LevelFilter::Debug));
125        assert_eq!(
126            METADATA.level_filter(1),
127            FilterState::Set(LevelFilter::Debug)
128        );
129        assert_eq!(
130            METADATA.level_filter(2),
131            FilterState::Set(LevelFilter::Debug)
132        );
133        METADATA.set_level_filter(1, Some(LevelFilter::Info));
134        assert_eq!(
135            METADATA.level_filter(2),
136            FilterState::Set(LevelFilter::Debug)
137        );
138        assert_eq!(METADATA.level_filter(3), FilterState::Outdated);
139        METADATA.set_level_filter(3, None);
140        assert_eq!(METADATA.level_filter(3), FilterState::NotSet);
141        METADATA.set_level_filter(3, Some(LevelFilter::Warn));
142        assert_eq!(METADATA.level_filter(3), FilterState::NotSet);
143
144        let mut threads = Vec::new();
145        for _ in 0..1 {
146            threads.push(thread::spawn(move || {
147                for i in 0..1024 {
148                    let filter = match i % 6 {
149                        0 => LevelFilter::Off,
150                        1 => LevelFilter::Error,
151                        2 => LevelFilter::Warn,
152                        3 => LevelFilter::Info,
153                        4 => LevelFilter::Debug,
154                        5 => LevelFilter::Trace,
155                        _ => unreachable!(),
156                    };
157
158                    METADATA.set_level_filter(i, Some(filter));
159                }
160            }));
161        }
162        for t in threads {
163            t.join().unwrap();
164        }
165        assert_eq!(
166            METADATA.level_filter(1023),
167            FilterState::Set(LevelFilter::Info)
168        );
169        assert_eq!(METADATA.level_filter(1024), FilterState::Outdated);
170    }
171}