ant_quic/logging/
filters.rs1use std::collections::HashMap;
12use tracing::Level;
13
14#[derive(Debug, Clone)]
16pub struct LogFilter {
17 component_levels: HashMap<String, Level>,
19 default_level: Level,
21 exclude_patterns: Vec<regex::Regex>,
23 include_patterns: Vec<regex::Regex>,
25}
26
27impl LogFilter {
28 pub fn new() -> Self {
30 Self {
31 component_levels: HashMap::new(),
32 default_level: Level::INFO,
33 exclude_patterns: Vec::new(),
34 include_patterns: Vec::new(),
35 }
36 }
37
38 pub fn with_default_level(mut self, level: Level) -> Self {
40 self.default_level = level;
41 self
42 }
43
44 pub fn with_module(mut self, module: &str, level: Level) -> Self {
46 self.component_levels.insert(module.to_string(), level);
47 self
48 }
49
50 pub fn exclude_pattern(mut self, pattern: &str) -> Result<Self, regex::Error> {
52 let regex = regex::Regex::new(pattern)?;
53 self.exclude_patterns.push(regex);
54 Ok(self)
55 }
56
57 pub fn include_pattern(mut self, pattern: &str) -> Result<Self, regex::Error> {
59 let regex = regex::Regex::new(pattern)?;
60 self.include_patterns.push(regex);
61 Ok(self)
62 }
63
64 pub fn should_log(&self, target: &str, level: Level, message: &str) -> bool {
66 for pattern in &self.include_patterns {
68 if pattern.is_match(message) || pattern.is_match(target) {
69 return true;
70 }
71 }
72
73 for pattern in &self.exclude_patterns {
75 if pattern.is_match(message) || pattern.is_match(target) {
76 return false;
77 }
78 }
79
80 let required_level = self.level_for(target).unwrap_or(self.default_level);
84 level <= required_level
85 }
86
87 pub fn level_for(&self, target: &str) -> Option<Level> {
89 if let Some(&level) = self.component_levels.get(target) {
91 return Some(level);
92 }
93
94 for (module, &level) in &self.component_levels {
96 if target.starts_with(module) {
97 return Some(level);
98 }
99 }
100
101 None
102 }
103}
104
105impl Default for LogFilter {
106 fn default() -> Self {
107 Self::new()
108 }
109}
110
111pub struct LogFilterBuilder {
113 filter: LogFilter,
114}
115
116impl Default for LogFilterBuilder {
117 fn default() -> Self {
118 Self::new()
119 }
120}
121
122impl LogFilterBuilder {
123 pub fn new() -> Self {
125 Self {
126 filter: LogFilter::new(),
127 }
128 }
129
130 pub fn default_level(mut self, level: Level) -> Self {
132 self.filter.default_level = level;
133 self
134 }
135
136 pub fn quic_defaults(mut self) -> Self {
138 self.filter
139 .component_levels
140 .insert("ant_quic::connection".to_string(), Level::DEBUG);
141 self.filter
142 .component_levels
143 .insert("ant_quic::endpoint".to_string(), Level::INFO);
144 self.filter
145 .component_levels
146 .insert("ant_quic::frame".to_string(), Level::TRACE);
147 self.filter
148 .component_levels
149 .insert("ant_quic::packet".to_string(), Level::TRACE);
150 self.filter
151 .component_levels
152 .insert("ant_quic::crypto".to_string(), Level::DEBUG);
153 self.filter
154 .component_levels
155 .insert("ant_quic::transport_params".to_string(), Level::DEBUG);
156 self
157 }
158
159 pub fn nat_traversal_debug(mut self) -> Self {
161 self.filter
162 .component_levels
163 .insert("ant_quic::nat_traversal".to_string(), Level::TRACE);
164 self.filter
165 .component_levels
166 .insert("ant_quic::candidate_discovery".to_string(), Level::DEBUG);
167 self.filter.component_levels.insert(
168 "ant_quic::connection::nat_traversal".to_string(),
169 Level::TRACE,
170 );
171 self
172 }
173
174 pub fn performance_analysis(mut self) -> Self {
176 self.filter
177 .component_levels
178 .insert("ant_quic::metrics".to_string(), Level::INFO);
179 self.filter
180 .component_levels
181 .insert("ant_quic::congestion".to_string(), Level::DEBUG);
182 self.filter
183 .component_levels
184 .insert("ant_quic::pacing".to_string(), Level::DEBUG);
185 self
186 }
187
188 pub fn production(mut self) -> Self {
190 self.filter.default_level = Level::WARN;
191 self.filter
192 .component_levels
193 .insert("ant_quic::connection::lifecycle".to_string(), Level::INFO);
194 self.filter
195 .component_levels
196 .insert("ant_quic::endpoint".to_string(), Level::INFO);
197 self.filter
198 .component_levels
199 .insert("ant_quic::metrics".to_string(), Level::INFO);
200 self
201 }
202
203 pub fn quiet(mut self) -> Self {
205 if let Ok(pattern) = regex::Regex::new(r"packet\.sent") {
207 self.filter.exclude_patterns.push(pattern);
208 }
209 if let Ok(pattern) = regex::Regex::new(r"packet\.received") {
210 self.filter.exclude_patterns.push(pattern);
211 }
212 if let Ok(pattern) = regex::Regex::new(r"frame\.sent") {
213 self.filter.exclude_patterns.push(pattern);
214 }
215 if let Ok(pattern) = regex::Regex::new(r"frame\.received") {
216 self.filter.exclude_patterns.push(pattern);
217 }
218 self
219 }
220
221 pub fn build(self) -> LogFilter {
223 self.filter
224 }
225}
226
227pub struct DynamicLogFilter {
229 inner: std::sync::RwLock<LogFilter>,
230}
231
232impl DynamicLogFilter {
233 pub fn new(filter: LogFilter) -> Self {
235 Self {
236 inner: std::sync::RwLock::new(filter),
237 }
238 }
239
240 #[allow(clippy::expect_used)]
242 pub fn update<F>(&self, updater: F) -> Result<(), Box<dyn std::error::Error>>
243 where
244 F: FnOnce(&mut LogFilter) -> Result<(), Box<dyn std::error::Error>>,
245 {
246 let mut filter = self
247 .inner
248 .write()
249 .expect("Mutex poisoning is unexpected in normal operation");
250 updater(&mut filter)?;
251 Ok(())
252 }
253
254 #[allow(clippy::unwrap_used, clippy::expect_used)]
256 pub fn should_log(&self, target: &str, level: Level, message: &str) -> bool {
257 self.inner
258 .read()
259 .expect("Mutex poisoning is unexpected in normal operation")
260 .should_log(target, level, message)
261 }
262
263 #[allow(clippy::unwrap_used, clippy::expect_used)]
265 pub fn level_for(&self, target: &str) -> Option<Level> {
266 self.inner
267 .read()
268 .expect("Mutex poisoning is unexpected in normal operation")
269 .level_for(target)
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use super::*;
276
277 #[test]
278 fn test_component_filtering() {
279 let filter = LogFilterBuilder::new()
280 .default_level(Level::WARN)
281 .quic_defaults()
282 .build();
283
284 assert!(filter.should_log("ant_quic::connection::mod", Level::DEBUG, "test"));
286 assert!(!filter.should_log("ant_quic::connection::mod", Level::TRACE, "test"));
287
288 assert!(filter.should_log("ant_quic::endpoint", Level::INFO, "test"));
290 assert!(!filter.should_log("ant_quic::endpoint", Level::DEBUG, "test"));
291
292 assert!(filter.should_log("other::module", Level::WARN, "test"));
294 assert!(!filter.should_log("other::module", Level::INFO, "test"));
295 }
296
297 #[test]
298 fn test_pattern_filtering() {
299 let filter = LogFilter::new()
300 .exclude_pattern(r"noisy")
301 .unwrap()
302 .include_pattern(r"important.*noisy")
303 .unwrap();
304
305 assert!(!filter.should_log("test", Level::INFO, "this is noisy"));
306 assert!(filter.should_log("test", Level::INFO, "this is important but noisy"));
307 assert!(filter.should_log("test", Level::INFO, "this is normal"));
308 }
309}