1use log::{Level, LevelFilter, Metadata, Record};
3use std::env;
4use std::fmt;
5use std::mem;
6
7pub struct Filter {
9 directives: Vec<Directive>,
10 filter: Option<InnerFilter>,
11}
12
13#[derive(Debug)]
14pub struct InnerFilter {
15 inner: String,
16}
17
18impl InnerFilter {
19 pub fn new(spec: &str) -> Result<Self, String> {
20 Ok(Self {
21 inner: spec.to_string(),
22 })
23 }
24
25 pub fn is_match(&self, s: &str) -> bool {
26 s.contains(&self.inner)
27 }
28}
29
30impl fmt::Display for InnerFilter {
31 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32 self.inner.fmt(f)
33 }
34}
35
36pub struct Builder {
38 directives: Vec<Directive>,
39 filter: Option<InnerFilter>,
40 built: bool,
41}
42
43#[derive(Debug)]
44struct Directive {
45 name: Option<String>,
46 level: LevelFilter,
47}
48
49impl Filter {
50 pub fn filter(&self) -> LevelFilter {
53 self.directives
54 .iter()
55 .map(|d| d.level)
56 .max()
57 .unwrap_or(LevelFilter::Off)
58 }
59
60 pub fn matches(&self, record: &Record) -> bool {
62 if !self.enabled(record.metadata()) {
63 return false;
64 }
65
66 if let Some(filter) = self.filter.as_ref() {
67 if !filter.is_match(&*record.args().to_string()) {
68 return false;
69 }
70 }
71
72 true
73 }
74
75 pub fn enabled(&self, metadata: &Metadata) -> bool {
77 let level = metadata.level();
78 let target = metadata.target();
79
80 enabled(&self.directives, level, target)
81 }
82}
83
84impl Builder {
85 pub fn new() -> Builder {
87 Builder {
88 directives: Vec::new(),
89 filter: None,
90 built: false,
91 }
92 }
93
94 pub fn from_env(env: &str) -> Builder {
96 let mut builder = Builder::new();
97
98 if let Ok(s) = env::var(env) {
99 builder.parse(&s);
100 }
101
102 builder
103 }
104
105 pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
107 self.filter(Some(module), level)
108 }
109
110 pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
112 self.filter(None, level)
113 }
114
115 pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self {
120 self.directives.push(Directive {
121 name: module.map(|s| s.to_string()),
122 level,
123 });
124 self
125 }
126
127 pub fn parse(&mut self, filters: &str) -> &mut Self {
129 let (directives, filter) = parse_spec(filters);
130
131 self.filter = filter;
132
133 for directive in directives {
134 self.directives.push(directive);
135 }
136 self
137 }
138
139 pub fn build(&mut self) -> Filter {
141 assert!(!self.built, "attempt to re-use consumed builder");
142 self.built = true;
143
144 if self.directives.is_empty() {
145 self.directives.push(Directive {
147 name: None,
148 level: LevelFilter::Error,
149 });
150 } else {
151 self.directives.sort_by(|a, b| {
154 let alen = a.name.as_ref().map(|a| a.len()).unwrap_or(0);
155 let blen = b.name.as_ref().map(|b| b.len()).unwrap_or(0);
156 alen.cmp(&blen)
157 });
158 }
159
160 Filter {
161 directives: mem::replace(&mut self.directives, Vec::new()),
162 filter: mem::replace(&mut self.filter, None),
163 }
164 }
165}
166
167impl Default for Builder {
168 fn default() -> Self {
169 Builder::new()
170 }
171}
172
173impl fmt::Debug for Filter {
174 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175 f.debug_struct("Filter")
176 .field("filter", &self.filter)
177 .field("directives", &self.directives)
178 .finish()
179 }
180}
181
182impl fmt::Debug for Builder {
183 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
184 if self.built {
185 f.debug_struct("Filter").field("built", &true).finish()
186 } else {
187 f.debug_struct("Filter")
188 .field("filter", &self.filter)
189 .field("directives", &self.directives)
190 .finish()
191 }
192 }
193}
194
195fn parse_spec(spec: &str) -> (Vec<Directive>, Option<InnerFilter>) {
198 let mut dirs = Vec::new();
199
200 let mut parts = spec.split('/');
201 let mods = parts.next();
202 let filter = parts.next();
203 if parts.next().is_some() {
204 eprintln!(
205 "warning: invalid logging spec '{}', \
206 ignoring it (too many '/'s)",
207 spec
208 );
209 return (dirs, None);
210 }
211 mods.map(|m| {
212 for s in m.split(',') {
213 if s.len() == 0 {
214 continue;
215 }
216 let mut parts = s.split('=');
217 let (log_level, name) =
218 match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) {
219 (Some(part0), None, None) => {
220 match part0.parse() {
223 Ok(num) => (num, None),
224 Err(_) => (LevelFilter::max(), Some(part0)),
225 }
226 }
227 (Some(part0), Some(""), None) => (LevelFilter::max(), Some(part0)),
228 (Some(part0), Some(part1), None) => match part1.parse() {
229 Ok(num) => (num, Some(part0)),
230 _ => {
231 eprintln!(
232 "warning: invalid logging spec '{}', \
233 ignoring it",
234 part1
235 );
236 continue;
237 }
238 },
239 _ => {
240 eprintln!(
241 "warning: invalid logging spec '{}', \
242 ignoring it",
243 s
244 );
245 continue;
246 }
247 };
248 dirs.push(Directive {
249 name: name.map(|s| s.to_string()),
250 level: log_level,
251 });
252 }
253 });
254
255 let filter = filter.map_or(None, |filter| match InnerFilter::new(filter) {
256 Ok(re) => Some(re),
257 Err(e) => {
258 eprintln!("warning: invalid regex filter - {}", e);
259 None
260 }
261 });
262
263 return (dirs, filter);
264}
265
266fn enabled(directives: &[Directive], level: Level, target: &str) -> bool {
268 for directive in directives.iter().rev() {
270 match directive.name {
271 Some(ref name) if !target.starts_with(&**name) => {}
272 Some(..) | None => return level <= directive.level,
273 }
274 }
275 false
276}
277
278#[cfg(test)]
279mod tests {
280 use log::{Level, LevelFilter};
281
282 use super::{enabled, parse_spec, Builder, Directive, Filter};
283
284 fn make_logger_filter(dirs: Vec<Directive>) -> Filter {
285 let mut logger = Builder::new().build();
286 logger.directives = dirs;
287 logger
288 }
289
290 #[test]
291 fn filter_info() {
292 let logger = Builder::new().filter(None, LevelFilter::Info).build();
293 assert!(enabled(&logger.directives, Level::Info, "crate1"));
294 assert!(!enabled(&logger.directives, Level::Debug, "crate1"));
295 }
296
297 #[test]
298 fn filter_beginning_longest_match() {
299 let logger = Builder::new()
300 .filter(Some("crate2"), LevelFilter::Info)
301 .filter(Some("crate2::mod"), LevelFilter::Debug)
302 .filter(Some("crate1::mod1"), LevelFilter::Warn)
303 .build();
304 assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
305 assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
306 }
307
308 #[test]
309 fn parse_default() {
310 let logger = Builder::new().parse("info,crate1::mod1=warn").build();
311 assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
312 assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
313 }
314
315 #[test]
316 fn match_full_path() {
317 let logger = make_logger_filter(vec![
318 Directive {
319 name: Some("crate2".to_string()),
320 level: LevelFilter::Info,
321 },
322 Directive {
323 name: Some("crate1::mod1".to_string()),
324 level: LevelFilter::Warn,
325 },
326 ]);
327 assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
328 assert!(!enabled(&logger.directives, Level::Info, "crate1::mod1"));
329 assert!(enabled(&logger.directives, Level::Info, "crate2"));
330 assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
331 }
332
333 #[test]
334 fn no_match() {
335 let logger = make_logger_filter(vec![
336 Directive {
337 name: Some("crate2".to_string()),
338 level: LevelFilter::Info,
339 },
340 Directive {
341 name: Some("crate1::mod1".to_string()),
342 level: LevelFilter::Warn,
343 },
344 ]);
345 assert!(!enabled(&logger.directives, Level::Warn, "crate3"));
346 }
347
348 #[test]
349 fn match_beginning() {
350 let logger = make_logger_filter(vec![
351 Directive {
352 name: Some("crate2".to_string()),
353 level: LevelFilter::Info,
354 },
355 Directive {
356 name: Some("crate1::mod1".to_string()),
357 level: LevelFilter::Warn,
358 },
359 ]);
360 assert!(enabled(&logger.directives, Level::Info, "crate2::mod1"));
361 }
362
363 #[test]
364 fn match_beginning_longest_match() {
365 let logger = make_logger_filter(vec![
366 Directive {
367 name: Some("crate2".to_string()),
368 level: LevelFilter::Info,
369 },
370 Directive {
371 name: Some("crate2::mod".to_string()),
372 level: LevelFilter::Debug,
373 },
374 Directive {
375 name: Some("crate1::mod1".to_string()),
376 level: LevelFilter::Warn,
377 },
378 ]);
379 assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
380 assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
381 }
382
383 #[test]
384 fn match_default() {
385 let logger = make_logger_filter(vec![
386 Directive {
387 name: None,
388 level: LevelFilter::Info,
389 },
390 Directive {
391 name: Some("crate1::mod1".to_string()),
392 level: LevelFilter::Warn,
393 },
394 ]);
395 assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
396 assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
397 }
398
399 #[test]
400 fn zero_level() {
401 let logger = make_logger_filter(vec![
402 Directive {
403 name: None,
404 level: LevelFilter::Info,
405 },
406 Directive {
407 name: Some("crate1::mod1".to_string()),
408 level: LevelFilter::Off,
409 },
410 ]);
411 assert!(!enabled(&logger.directives, Level::Error, "crate1::mod1"));
412 assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
413 }
414
415 #[test]
416 fn parse_spec_valid() {
417 let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug");
418 assert_eq!(dirs.len(), 3);
419 assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
420 assert_eq!(dirs[0].level, LevelFilter::Error);
421
422 assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
423 assert_eq!(dirs[1].level, LevelFilter::max());
424
425 assert_eq!(dirs[2].name, Some("crate2".to_string()));
426 assert_eq!(dirs[2].level, LevelFilter::Debug);
427 assert!(filter.is_none());
428 }
429
430 #[test]
431 fn parse_spec_invalid_crate() {
432 let (dirs, filter) = parse_spec("crate1::mod1=warn=info,crate2=debug");
434 assert_eq!(dirs.len(), 1);
435 assert_eq!(dirs[0].name, Some("crate2".to_string()));
436 assert_eq!(dirs[0].level, LevelFilter::Debug);
437 assert!(filter.is_none());
438 }
439
440 #[test]
441 fn parse_spec_invalid_level() {
442 let (dirs, filter) = parse_spec("crate1::mod1=noNumber,crate2=debug");
444 assert_eq!(dirs.len(), 1);
445 assert_eq!(dirs[0].name, Some("crate2".to_string()));
446 assert_eq!(dirs[0].level, LevelFilter::Debug);
447 assert!(filter.is_none());
448 }
449
450 #[test]
451 fn parse_spec_string_level() {
452 let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=warn");
454 assert_eq!(dirs.len(), 1);
455 assert_eq!(dirs[0].name, Some("crate2".to_string()));
456 assert_eq!(dirs[0].level, LevelFilter::Warn);
457 assert!(filter.is_none());
458 }
459
460 #[test]
461 fn parse_spec_empty_level() {
462 let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=");
464 assert_eq!(dirs.len(), 1);
465 assert_eq!(dirs[0].name, Some("crate2".to_string()));
466 assert_eq!(dirs[0].level, LevelFilter::max());
467 assert!(filter.is_none());
468 }
469
470 #[test]
471 fn parse_spec_global() {
472 let (dirs, filter) = parse_spec("warn,crate2=debug");
474 assert_eq!(dirs.len(), 2);
475 assert_eq!(dirs[0].name, None);
476 assert_eq!(dirs[0].level, LevelFilter::Warn);
477 assert_eq!(dirs[1].name, Some("crate2".to_string()));
478 assert_eq!(dirs[1].level, LevelFilter::Debug);
479 assert!(filter.is_none());
480 }
481
482 #[test]
483 fn parse_spec_valid_filter() {
484 let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug/abc");
485 assert_eq!(dirs.len(), 3);
486 assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
487 assert_eq!(dirs[0].level, LevelFilter::Error);
488
489 assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
490 assert_eq!(dirs[1].level, LevelFilter::max());
491
492 assert_eq!(dirs[2].name, Some("crate2".to_string()));
493 assert_eq!(dirs[2].level, LevelFilter::Debug);
494 assert!(filter.is_some() && filter.unwrap().to_string() == "abc");
495 }
496
497 #[test]
498 fn parse_spec_invalid_crate_filter() {
499 let (dirs, filter) = parse_spec("crate1::mod1=error=warn,crate2=debug/a.c");
500 assert_eq!(dirs.len(), 1);
501 assert_eq!(dirs[0].name, Some("crate2".to_string()));
502 assert_eq!(dirs[0].level, LevelFilter::Debug);
503 assert!(filter.is_some() && filter.unwrap().to_string() == "a.c");
504 }
505
506 #[test]
507 fn parse_spec_empty_with_filter() {
508 let (dirs, filter) = parse_spec("crate1/a*c");
509 assert_eq!(dirs.len(), 1);
510 assert_eq!(dirs[0].name, Some("crate1".to_string()));
511 assert_eq!(dirs[0].level, LevelFilter::max());
512 assert!(filter.is_some() && filter.unwrap().to_string() == "a*c");
513 }
514}