1#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
70 html_favicon_url = "http://www.rust-lang.org/favicon.ico",
71 html_root_url = "http://doc.rust-lang.org/env_logger/")]
72#![cfg_attr(test, deny(warnings))]
73
74extern crate slog;
75extern crate slog_term;
76extern crate slog_stdlog;
77extern crate slog_scope;
78extern crate log;
79
80use std::{env, result, sync};
81use std::cell::RefCell;
82use slog::*;
83
84#[cfg(feature = "regex")]
85#[path = "regex.rs"]
86mod filter;
87
88#[cfg(not(feature = "regex"))]
89#[path = "string.rs"]
90mod filter;
91
92thread_local! {
93 static TL_BUF: RefCell<String> = RefCell::new(String::new())
94}
95
96pub struct EnvLogger<T : Drain> {
98 drain : T,
99 directives: Vec<LogDirective>,
100 filter: Option<filter::Filter>,
101}
102
103pub struct LogBuilder<T : Drain> {
107 drain : T,
108 directives: Vec<LogDirective>,
109 filter: Option<filter::Filter>,
110}
111
112impl<T : Drain> LogBuilder<T> {
113 pub fn new(d : T) -> Self {
115 LogBuilder {
116 drain : d,
117 directives: Vec::new(),
118 filter: None,
119 }
120 }
121
122 pub fn filter(mut self,
127 module: Option<&str>,
128 level: FilterLevel) -> Self {
129 self.directives.push(LogDirective {
130 name: module.map(|s| s.to_string()),
131 level: level,
132 });
133 self
134 }
135
136 pub fn parse(mut self, filters: &str) -> Self {
141 let (directives, filter) = parse_logging_spec(filters);
142
143 self.filter = filter;
144
145 for directive in directives {
146 self.directives.push(directive);
147 }
148 self
149 }
150
151 pub fn build(mut self) -> EnvLogger<T> {
153 if self.directives.is_empty() {
154 self.directives.push(LogDirective {
156 name: None,
157 level: FilterLevel::Error,
158 });
159 } else {
160 self.directives.sort_by(|a, b| {
163 let alen = a.name.as_ref().map(|a| a.len()).unwrap_or(0);
164 let blen = b.name.as_ref().map(|b| b.len()).unwrap_or(0);
165 alen.cmp(&blen)
166 });
167 }
168
169 let LogBuilder {
170 drain,
171 directives,
172 filter,
173 } = self;
174
175 EnvLogger {
176 drain: drain,
177 directives: directives,
178 filter: filter,
179 }
180 }
181}
182
183impl<T : Drain> EnvLogger<T> {
184 pub fn new(d : T) -> Self {
185 let mut builder = LogBuilder::new(d);
186
187 if let Ok(s) = env::var("RUST_LOG") {
188 builder = builder.parse(&s);
189 }
190
191 builder.build()
192 }
193
194 pub fn filter(&self) -> FilterLevel {
195 self.directives.iter()
196 .map(|d| d.level).max()
197 .unwrap_or(FilterLevel::Off)
198 }
199
200 fn enabled(&self, level: Level, module: &str) -> bool {
201 for directive in self.directives.iter().rev() {
203 match directive.name {
204 Some(ref name) if !module.starts_with(&**name) => {},
205 Some(..) | None => {
206 return level.as_usize() <= directive.level.as_usize()
207 }
208 }
209 }
210 false
211 }
212}
213
214impl<T : Drain> Drain for EnvLogger<T>
215where T : Drain<Ok=()> {
216 type Err = T::Err;
217 type Ok = ();
218 fn log(&self, info: &Record, val : &OwnedKVList) -> result::Result<(), T::Err> {
219 if !self.enabled(info.level(), info.module()) {
220 return Ok(());
221 }
222
223 if let Some(filter) = self.filter.as_ref() {
224 if !filter.is_match(&format!("{}", info.msg())) {
225 return Ok(())
226 }
227 }
228
229 TL_BUF.with(|buf| {
230 let mut buf = buf.borrow_mut();
231 let res = self.drain.log(info, val);
232 buf.clear();
233 res
234 })
235 }
236}
237
238struct LogDirective {
239 name: Option<String>,
240 level: FilterLevel,
241}
242
243pub fn new<T : Drain>(d : T) -> EnvLogger<T> {
245 let mut builder = LogBuilder::new(d);
246
247 if let Ok(s) = env::var("RUST_LOG") {
248 builder = builder.parse(&s);
249 }
250
251 builder.build()
252}
253
254pub fn init() -> std::result::Result<slog_scope::GlobalLoggerGuard, log::SetLoggerError> {
264 let drain = slog_term::CompactFormat::new(
265 slog_term::TermDecorator::new().stderr().build()
266 ).build();
267 let drain = new(drain);
268 let drain = sync::Mutex::new(drain.fuse());
269
270 let guard = slog_scope::set_global_logger(Logger::root(drain.fuse(), o!()).into_erased());
271 slog_stdlog::init()?;
272
273 Ok(guard)
274}
275
276fn parse_logging_spec(spec: &str) -> (Vec<LogDirective>, Option<filter::Filter>) {
279 let mut dirs = Vec::new();
280
281 let mut parts = spec.split('/');
282 let mods = parts.next();
283 let filter = parts.next();
284 if parts.next().is_some() {
285 println!("warning: invalid logging spec '{}', \
286 ignoring it (too many '/'s)", spec);
287 return (dirs, None);
288 }
289 mods.map(|m| { for s in m.split(',') {
290 if s.len() == 0 { continue }
291 let mut parts = s.split('=');
292 let (log_level, name) = match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) {
293 (Some(part0), None, None) => {
294 match part0.parse() {
297 Ok(num) => (num, None),
298 Err(_) => (FilterLevel::max(), Some(part0)),
299 }
300 }
301 (Some(part0), Some(""), None) => (FilterLevel::max(), Some(part0)),
302 (Some(part0), Some(part1), None) => {
303 match part1.parse() {
304 Ok(num) => (num, Some(part0)),
305 _ => {
306 println!("warning: invalid logging spec '{}', \
307 ignoring it", part1);
308 continue
309 }
310 }
311 },
312 _ => {
313 println!("warning: invalid logging spec '{}', \
314 ignoring it", s);
315 continue
316 }
317 };
318 dirs.push(LogDirective {
319 name: name.map(|s| s.to_string()),
320 level: log_level,
321 });
322 }});
323
324 let filter = filter.map_or(None, |filter| {
325 match filter::Filter::new(filter) {
326 Ok(re) => Some(re),
327 Err(e) => {
328 println!("warning: invalid regex filter - {}", e);
329 None
330 }
331 }
332 });
333
334 return (dirs, filter);
335}
336
337#[cfg(test)]
338mod tests {
339 use slog::{Level, FilterLevel};
340 use super::slog;
341
342 use super::{LogBuilder, EnvLogger, LogDirective, parse_logging_spec};
343
344 fn make_logger(dirs: Vec<LogDirective>) -> EnvLogger<slog::Discard> {
345 let mut logger = LogBuilder::new(slog::Discard).build();
346 logger.directives = dirs;
347 logger
348 }
349
350 #[test]
351 fn filter_info() {
352 let logger = LogBuilder::new(slog::Discard).filter(None, FilterLevel::Info).build();
353 assert!(logger.enabled(Level::Info, "crate1"));
354 assert!(!logger.enabled(Level::Debug, "crate1"));
355 }
356
357 #[test]
358 fn filter_beginning_longest_match() {
359 let logger = LogBuilder::new(slog::Discard)
360 .filter(Some("crate2"), FilterLevel::Info)
361 .filter(Some("crate2::mod"), FilterLevel::Debug)
362 .filter(Some("crate1::mod1"), FilterLevel::Warning)
363 .build();
364 assert!(logger.enabled(Level::Debug, "crate2::mod1"));
365 assert!(!logger.enabled(Level::Debug, "crate2"));
366 }
367
368 #[test]
369 fn parse_default() {
370 let logger = LogBuilder::new(slog::Discard).parse("info,crate1::mod1=warn").build();
371 assert!(logger.enabled(Level::Warning, "crate1::mod1"));
372 assert!(logger.enabled(Level::Info, "crate2::mod2"));
373 }
374
375 #[test]
376 fn match_full_path() {
377 let logger = make_logger(vec![
378 LogDirective {
379 name: Some("crate2".to_string()),
380 level: FilterLevel::Info
381 },
382 LogDirective {
383 name: Some("crate1::mod1".to_string()),
384 level: FilterLevel::Warning
385 }
386 ]);
387 assert!(logger.enabled(Level::Warning, "crate1::mod1"));
388 assert!(!logger.enabled(Level::Info, "crate1::mod1"));
389 assert!(logger.enabled(Level::Info, "crate2"));
390 assert!(!logger.enabled(Level::Debug, "crate2"));
391 }
392
393 #[test]
394 fn no_match() {
395 let logger = make_logger(vec![
396 LogDirective { name: Some("crate2".to_string()), level: FilterLevel::Info },
397 LogDirective { name: Some("crate1::mod1".to_string()), level: FilterLevel::Warning }
398 ]);
399 assert!(!logger.enabled(Level::Warning, "crate3"));
400 }
401
402 #[test]
403 fn match_beginning() {
404 let logger = make_logger(vec![
405 LogDirective { name: Some("crate2".to_string()), level: FilterLevel::Info },
406 LogDirective { name: Some("crate1::mod1".to_string()), level: FilterLevel::Warning }
407 ]);
408 assert!(logger.enabled(Level::Info, "crate2::mod1"));
409 }
410
411 #[test]
412 fn match_beginning_longest_match() {
413 let logger = make_logger(vec![
414 LogDirective { name: Some("crate2".to_string()), level: FilterLevel::Info },
415 LogDirective { name: Some("crate2::mod".to_string()), level: FilterLevel::Debug },
416 LogDirective { name: Some("crate1::mod1".to_string()), level: FilterLevel::Warning }
417 ]);
418 assert!(logger.enabled(Level::Debug, "crate2::mod1"));
419 assert!(!logger.enabled(Level::Debug, "crate2"));
420 }
421
422 #[test]
423 fn match_default() {
424 let logger = make_logger(vec![
425 LogDirective { name: None, level: FilterLevel::Info },
426 LogDirective { name: Some("crate1::mod1".to_string()), level: FilterLevel::Warning }
427 ]);
428 assert!(logger.enabled(Level::Warning, "crate1::mod1"));
429 assert!(logger.enabled(Level::Info, "crate2::mod2"));
430 }
431
432 #[test]
433 fn zero_level() {
434 let logger = make_logger(vec![
435 LogDirective { name: None, level: FilterLevel::Info },
436 LogDirective { name: Some("crate1::mod1".to_string()), level: FilterLevel::Off }
437 ]);
438 assert!(!logger.enabled(Level::Error, "crate1::mod1"));
439 assert!(logger.enabled(Level::Info, "crate2::mod2"));
440 }
441
442 #[test]
443 fn parse_logging_spec_valid() {
444 let (dirs, filter) = parse_logging_spec("crate1::mod1=error,crate1::mod2,crate2=debug");
445 assert_eq!(dirs.len(), 3);
446 assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
447 assert_eq!(dirs[0].level, FilterLevel::Error);
448
449 assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
450 assert_eq!(dirs[1].level, FilterLevel::max());
451
452 assert_eq!(dirs[2].name, Some("crate2".to_string()));
453 assert_eq!(dirs[2].level, FilterLevel::Debug);
454 assert!(filter.is_none());
455 }
456
457 #[test]
458 fn parse_logging_spec_invalid_crate() {
459 let (dirs, filter) = parse_logging_spec("crate1::mod1=warn=info,crate2=debug");
461 assert_eq!(dirs.len(), 1);
462 assert_eq!(dirs[0].name, Some("crate2".to_string()));
463 assert_eq!(dirs[0].level, FilterLevel::Debug);
464 assert!(filter.is_none());
465 }
466
467 #[test]
468 fn parse_logging_spec_invalid_log_level() {
469 let (dirs, filter) = parse_logging_spec("crate1::mod1=noNumber,crate2=debug");
471 assert_eq!(dirs.len(), 1);
472 assert_eq!(dirs[0].name, Some("crate2".to_string()));
473 assert_eq!(dirs[0].level, FilterLevel::Debug);
474 assert!(filter.is_none());
475 }
476
477 #[test]
478 fn parse_logging_spec_string_log_level() {
479 let (dirs, filter) = parse_logging_spec("crate1::mod1=wrong,crate2=warn");
481 assert_eq!(dirs.len(), 1);
482 assert_eq!(dirs[0].name, Some("crate2".to_string()));
483 assert_eq!(dirs[0].level, FilterLevel::Warning);
484 assert!(filter.is_none());
485 }
486
487 #[test]
488 fn parse_logging_spec_empty_log_level() {
489 let (dirs, filter) = parse_logging_spec("crate1::mod1=wrong,crate2=");
491 assert_eq!(dirs.len(), 1);
492 assert_eq!(dirs[0].name, Some("crate2".to_string()));
493 assert_eq!(dirs[0].level, FilterLevel::max());
494 assert!(filter.is_none());
495 }
496
497 #[test]
498 fn parse_logging_spec_global() {
499 let (dirs, filter) = parse_logging_spec("warn,crate2=debug");
501 assert_eq!(dirs.len(), 2);
502 assert_eq!(dirs[0].name, None);
503 assert_eq!(dirs[0].level, FilterLevel::Warning);
504 assert_eq!(dirs[1].name, Some("crate2".to_string()));
505 assert_eq!(dirs[1].level, FilterLevel::Debug);
506 assert!(filter.is_none());
507 }
508
509 #[test]
510 fn parse_logging_spec_valid_filter() {
511 let (dirs, filter) = parse_logging_spec("crate1::mod1=error,crate1::mod2,crate2=debug/abc");
512 assert_eq!(dirs.len(), 3);
513 assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
514 assert_eq!(dirs[0].level, FilterLevel::Error);
515
516 assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
517 assert_eq!(dirs[1].level, FilterLevel::max());
518
519 assert_eq!(dirs[2].name, Some("crate2".to_string()));
520 assert_eq!(dirs[2].level, FilterLevel::Debug);
521 assert!(filter.is_some() && filter.unwrap().to_string() == "abc");
522 }
523
524 #[test]
525 fn parse_logging_spec_invalid_crate_filter() {
526 let (dirs, filter) = parse_logging_spec("crate1::mod1=error=warn,crate2=debug/a.c");
527 assert_eq!(dirs.len(), 1);
528 assert_eq!(dirs[0].name, Some("crate2".to_string()));
529 assert_eq!(dirs[0].level, FilterLevel::Debug);
530 assert!(filter.is_some() && filter.unwrap().to_string() == "a.c");
531 }
532
533 #[test]
534 fn parse_logging_spec_empty_with_filter() {
535 let (dirs, filter) = parse_logging_spec("crate1/a*c");
536 assert_eq!(dirs.len(), 1);
537 assert_eq!(dirs[0].name, Some("crate1".to_string()));
538 assert_eq!(dirs[0].level, FilterLevel::max());
539 assert!(filter.is_some() && filter.unwrap().to_string() == "a*c");
540 }
541}