1use log::{Level, LevelFilter, Metadata, Record};
63use std::env;
64use std::fmt;
65use std::mem;
66
67#[cfg(feature = "regex")]
68#[path = "regex.rs"]
69mod inner;
70
71#[cfg(not(feature = "regex"))]
72#[path = "string.rs"]
73mod inner;
74
75pub struct Filter {
83 directives: Vec<Directive>,
84 filter: Option<inner::Filter>,
85}
86
87pub struct Builder {
117 directives: Vec<Directive>,
118 filter: Option<inner::Filter>,
119 built: bool,
120}
121
122#[derive(Debug)]
123struct Directive {
124 name: Option<String>,
125 level: LevelFilter,
126}
127
128impl Filter {
129 pub fn filter(&self) -> LevelFilter {
151 self.directives
152 .iter()
153 .map(|d| d.level)
154 .max()
155 .unwrap_or(LevelFilter::Off)
156 }
157
158 pub fn matches(&self, record: &Record) -> bool {
160 if !self.enabled(record.metadata()) {
161 return false;
162 }
163
164 if let Some(filter) = self.filter.as_ref() {
165 if !filter.is_match(&*record.args().to_string()) {
166 return false;
167 }
168 }
169
170 true
171 }
172
173 pub fn enabled(&self, metadata: &Metadata) -> bool {
175 let level = metadata.level();
176 let target = metadata.target();
177
178 enabled(&self.directives, level, target)
179 }
180}
181
182impl Builder {
183 pub fn new() -> Builder {
185 Builder {
186 directives: Vec::new(),
187 filter: None,
188 built: false,
189 }
190 }
191
192 pub fn from_env(env: &str) -> Builder {
194 let mut builder = Builder::new();
195
196 if let Ok(s) = env::var(env) {
197 builder.parse(&s);
198 }
199
200 builder
201 }
202
203 pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
205 self.filter(Some(module), level)
206 }
207
208 pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
210 self.filter(None, level)
211 }
212
213 pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self {
218 self.directives.push(Directive {
219 name: module.map(|s| s.to_string()),
220 level,
221 });
222 self
223 }
224
225 pub fn parse(&mut self, filters: &str) -> &mut Self {
231 let (directives, filter) = parse_spec(filters);
232
233 self.filter = filter;
234
235 for directive in directives {
236 self.directives.push(directive);
237 }
238 self
239 }
240
241 pub fn build(&mut self) -> Filter {
243 assert!(!self.built, "attempt to re-use consumed builder");
244 self.built = true;
245
246 if self.directives.is_empty() {
247 self.directives.push(Directive {
249 name: None,
250 level: LevelFilter::Error,
251 });
252 } else {
253 self.directives.sort_by(|a, b| {
256 let alen = a.name.as_ref().map(|a| a.len()).unwrap_or(0);
257 let blen = b.name.as_ref().map(|b| b.len()).unwrap_or(0);
258 alen.cmp(&blen)
259 });
260 }
261
262 Filter {
263 directives: mem::replace(&mut self.directives, Vec::new()),
264 filter: mem::replace(&mut self.filter, None),
265 }
266 }
267}
268
269impl Default for Builder {
270 fn default() -> Self {
271 Builder::new()
272 }
273}
274
275impl fmt::Debug for Filter {
276 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
277 f.debug_struct("Filter")
278 .field("filter", &self.filter)
279 .field("directives", &self.directives)
280 .finish()
281 }
282}
283
284impl fmt::Debug for Builder {
285 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
286 if self.built {
287 f.debug_struct("Filter").field("built", &true).finish()
288 } else {
289 f.debug_struct("Filter")
290 .field("filter", &self.filter)
291 .field("directives", &self.directives)
292 .finish()
293 }
294 }
295}
296
297fn parse_spec(spec: &str) -> (Vec<Directive>, Option<inner::Filter>) {
300 let mut dirs = Vec::new();
301
302 let mut parts = spec.split('/');
303 let mods = parts.next();
304 let filter = parts.next();
305 if parts.next().is_some() {
306 eprintln!(
307 "warning: invalid logging spec '{}', \
308 ignoring it (too many '/'s)",
309 spec
310 );
311 return (dirs, None);
312 }
313 mods.map(|m| {
314 for s in m.split(',') {
315 if s.len() == 0 {
316 continue;
317 }
318 let mut parts = s.split('=');
319 let (log_level, name) =
320 match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) {
321 (Some(part0), None, None) => {
322 match part0.parse() {
325 Ok(num) => (num, None),
326 Err(_) => (LevelFilter::max(), Some(part0)),
327 }
328 }
329 (Some(part0), Some(""), None) => (LevelFilter::max(), Some(part0)),
330 (Some(part0), Some(part1), None) => match part1.parse() {
331 Ok(num) => (num, Some(part0)),
332 _ => {
333 eprintln!(
334 "warning: invalid logging spec '{}', \
335 ignoring it",
336 part1
337 );
338 continue;
339 }
340 },
341 _ => {
342 eprintln!(
343 "warning: invalid logging spec '{}', \
344 ignoring it",
345 s
346 );
347 continue;
348 }
349 };
350 dirs.push(Directive {
351 name: name.map(|s| s.to_string()),
352 level: log_level,
353 });
354 }
355 });
356
357 let filter = filter.map_or(None, |filter| match inner::Filter::new(filter) {
358 Ok(re) => Some(re),
359 Err(e) => {
360 eprintln!("warning: invalid regex filter - {}", e);
361 None
362 }
363 });
364
365 return (dirs, filter);
366}
367
368fn enabled(directives: &[Directive], level: Level, target: &str) -> bool {
370 for directive in directives.iter().rev() {
372 match directive.name {
373 Some(ref name) if !target.starts_with(&**name) => {}
374 Some(..) | None => return level <= directive.level,
375 }
376 }
377 false
378}
379
380#[cfg(test)]
381mod tests {
382 use log::{Level, LevelFilter};
383
384 use super::{enabled, parse_spec, Builder, Directive, Filter};
385
386 fn make_logger_filter(dirs: Vec<Directive>) -> Filter {
387 let mut logger = Builder::new().build();
388 logger.directives = dirs;
389 logger
390 }
391
392 #[test]
393 fn filter_info() {
394 let logger = Builder::new().filter(None, LevelFilter::Info).build();
395 assert!(enabled(&logger.directives, Level::Info, "crate1"));
396 assert!(!enabled(&logger.directives, Level::Debug, "crate1"));
397 }
398
399 #[test]
400 fn filter_beginning_longest_match() {
401 let logger = Builder::new()
402 .filter(Some("crate2"), LevelFilter::Info)
403 .filter(Some("crate2::mod"), LevelFilter::Debug)
404 .filter(Some("crate1::mod1"), LevelFilter::Warn)
405 .build();
406 assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
407 assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
408 }
409
410 #[test]
411 fn parse_default() {
412 let logger = Builder::new().parse("info,crate1::mod1=warn").build();
413 assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
414 assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
415 }
416
417 #[test]
418 fn match_full_path() {
419 let logger = make_logger_filter(vec![
420 Directive {
421 name: Some("crate2".to_string()),
422 level: LevelFilter::Info,
423 },
424 Directive {
425 name: Some("crate1::mod1".to_string()),
426 level: LevelFilter::Warn,
427 },
428 ]);
429 assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
430 assert!(!enabled(&logger.directives, Level::Info, "crate1::mod1"));
431 assert!(enabled(&logger.directives, Level::Info, "crate2"));
432 assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
433 }
434
435 #[test]
436 fn no_match() {
437 let logger = make_logger_filter(vec![
438 Directive {
439 name: Some("crate2".to_string()),
440 level: LevelFilter::Info,
441 },
442 Directive {
443 name: Some("crate1::mod1".to_string()),
444 level: LevelFilter::Warn,
445 },
446 ]);
447 assert!(!enabled(&logger.directives, Level::Warn, "crate3"));
448 }
449
450 #[test]
451 fn match_beginning() {
452 let logger = make_logger_filter(vec![
453 Directive {
454 name: Some("crate2".to_string()),
455 level: LevelFilter::Info,
456 },
457 Directive {
458 name: Some("crate1::mod1".to_string()),
459 level: LevelFilter::Warn,
460 },
461 ]);
462 assert!(enabled(&logger.directives, Level::Info, "crate2::mod1"));
463 }
464
465 #[test]
466 fn match_beginning_longest_match() {
467 let logger = make_logger_filter(vec![
468 Directive {
469 name: Some("crate2".to_string()),
470 level: LevelFilter::Info,
471 },
472 Directive {
473 name: Some("crate2::mod".to_string()),
474 level: LevelFilter::Debug,
475 },
476 Directive {
477 name: Some("crate1::mod1".to_string()),
478 level: LevelFilter::Warn,
479 },
480 ]);
481 assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
482 assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
483 }
484
485 #[test]
486 fn match_default() {
487 let logger = make_logger_filter(vec![
488 Directive {
489 name: None,
490 level: LevelFilter::Info,
491 },
492 Directive {
493 name: Some("crate1::mod1".to_string()),
494 level: LevelFilter::Warn,
495 },
496 ]);
497 assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
498 assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
499 }
500
501 #[test]
502 fn zero_level() {
503 let logger = make_logger_filter(vec![
504 Directive {
505 name: None,
506 level: LevelFilter::Info,
507 },
508 Directive {
509 name: Some("crate1::mod1".to_string()),
510 level: LevelFilter::Off,
511 },
512 ]);
513 assert!(!enabled(&logger.directives, Level::Error, "crate1::mod1"));
514 assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
515 }
516
517 #[test]
518 fn parse_spec_valid() {
519 let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug");
520 assert_eq!(dirs.len(), 3);
521 assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
522 assert_eq!(dirs[0].level, LevelFilter::Error);
523
524 assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
525 assert_eq!(dirs[1].level, LevelFilter::max());
526
527 assert_eq!(dirs[2].name, Some("crate2".to_string()));
528 assert_eq!(dirs[2].level, LevelFilter::Debug);
529 assert!(filter.is_none());
530 }
531
532 #[test]
533 fn parse_spec_invalid_crate() {
534 let (dirs, filter) = parse_spec("crate1::mod1=warn=info,crate2=debug");
536 assert_eq!(dirs.len(), 1);
537 assert_eq!(dirs[0].name, Some("crate2".to_string()));
538 assert_eq!(dirs[0].level, LevelFilter::Debug);
539 assert!(filter.is_none());
540 }
541
542 #[test]
543 fn parse_spec_invalid_level() {
544 let (dirs, filter) = parse_spec("crate1::mod1=noNumber,crate2=debug");
546 assert_eq!(dirs.len(), 1);
547 assert_eq!(dirs[0].name, Some("crate2".to_string()));
548 assert_eq!(dirs[0].level, LevelFilter::Debug);
549 assert!(filter.is_none());
550 }
551
552 #[test]
553 fn parse_spec_string_level() {
554 let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=warn");
556 assert_eq!(dirs.len(), 1);
557 assert_eq!(dirs[0].name, Some("crate2".to_string()));
558 assert_eq!(dirs[0].level, LevelFilter::Warn);
559 assert!(filter.is_none());
560 }
561
562 #[test]
563 fn parse_spec_empty_level() {
564 let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=");
566 assert_eq!(dirs.len(), 1);
567 assert_eq!(dirs[0].name, Some("crate2".to_string()));
568 assert_eq!(dirs[0].level, LevelFilter::max());
569 assert!(filter.is_none());
570 }
571
572 #[test]
573 fn parse_spec_global() {
574 let (dirs, filter) = parse_spec("warn,crate2=debug");
576 assert_eq!(dirs.len(), 2);
577 assert_eq!(dirs[0].name, None);
578 assert_eq!(dirs[0].level, LevelFilter::Warn);
579 assert_eq!(dirs[1].name, Some("crate2".to_string()));
580 assert_eq!(dirs[1].level, LevelFilter::Debug);
581 assert!(filter.is_none());
582 }
583
584 #[test]
585 fn parse_spec_valid_filter() {
586 let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug/abc");
587 assert_eq!(dirs.len(), 3);
588 assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
589 assert_eq!(dirs[0].level, LevelFilter::Error);
590
591 assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
592 assert_eq!(dirs[1].level, LevelFilter::max());
593
594 assert_eq!(dirs[2].name, Some("crate2".to_string()));
595 assert_eq!(dirs[2].level, LevelFilter::Debug);
596 assert!(filter.is_some() && filter.unwrap().to_string() == "abc");
597 }
598
599 #[test]
600 fn parse_spec_invalid_crate_filter() {
601 let (dirs, filter) = parse_spec("crate1::mod1=error=warn,crate2=debug/a.c");
602 assert_eq!(dirs.len(), 1);
603 assert_eq!(dirs[0].name, Some("crate2".to_string()));
604 assert_eq!(dirs[0].level, LevelFilter::Debug);
605 assert!(filter.is_some() && filter.unwrap().to_string() == "a.c");
606 }
607
608 #[test]
609 fn parse_spec_empty_with_filter() {
610 let (dirs, filter) = parse_spec("crate1/a*c");
611 assert_eq!(dirs.len(), 1);
612 assert_eq!(dirs[0].name, Some("crate1".to_string()));
613 assert_eq!(dirs[0].level, LevelFilter::max());
614 assert!(filter.is_some() && filter.unwrap().to_string() == "a*c");
615 }
616}