Skip to main content

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}