micromegas_tracing/logs/
events.rs1use 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 Outdated,
21 NotSet,
23 Set(LevelFilter),
25}
26
27impl LogMetadata<'_> {
28 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 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 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}