1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use std::ascii::AsciiExt;
14 #[derive(Debug, Clone)]
15 pub struct LogDirective {
16 pub name: Option<String>,
20 pub const LOG_LEVEL_NAMES: [&'static str; 4] = ["ERROR", "WARN", "INFO", "DEBUG"];
22 /// Parse an individual log level that is either a number or a symbolic log level
23 fn parse_log_level(level: &str) -> Option<u32> {
27 let pos = LOG_LEVEL_NAMES.iter().position(|&name| name.eq_ignore_ascii_case(level));
28 pos.map(|p| p as u32 + 1)
30 .map(|p| cmp::min(p, ::MAX_LOG_LEVEL))
33 /// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=1/foo")
34 /// and return a vector with log directives.
36 /// Valid log levels are 0-255, with the most likely ones being 1-4 (defined in
37 /// std::). Also supports string log levels of error, warn, info, and debug
38 pub fn parse_logging_spec(spec: &str) -> (Vec<LogDirective>, Option<String>) {
39 let mut dirs = Vec::new();
41 let mut parts = spec.split('/');
42 let mods = parts.next();
43 let filter = parts.next();
44 if parts.next().is_some() {
45 println!("warning: invalid logging spec '{}', ignoring it (too many '/'s)",
49 if let Some(m) = mods {
50 for s in m.split(',') {
54 let mut parts = s.split('=');
55 let (log_level, name) = match (parts.next(),
56 parts.next().map(|s| s.trim()),
58 (Some(part0), None, None) => {
59 // if the single argument is a log-level string or number,
60 // treat that as a global fallback
61 match parse_log_level(part0) {
62 Some(num) => (num, None),
63 None => (::MAX_LOG_LEVEL, Some(part0)),
66 (Some(part0), Some(""), None) => (::MAX_LOG_LEVEL, Some(part0)),
67 (Some(part0), Some(part1), None) => {
68 match parse_log_level(part1) {
69 Some(num) => (num, Some(part0)),
71 println!("warning: invalid logging spec '{}', ignoring it", part1);
77 println!("warning: invalid logging spec '{}', ignoring it", s);
81 dirs.push(LogDirective {
82 name: name.map(str::to_owned),
88 (dirs, filter.map(str::to_owned))
93 use super::parse_logging_spec;
96 fn parse_logging_spec_valid() {
97 let (dirs, filter) = parse_logging_spec("crate1::mod1=1,crate1::mod2,crate2=4");
98 assert_eq!(dirs.len(), 3);
99 assert_eq!(dirs[0].name, Some("crate1::mod1".to_owned()));
100 assert_eq!(dirs[0].level, 1);
102 assert_eq!(dirs[1].name, Some("crate1::mod2".to_owned()));
103 assert_eq!(dirs[1].level, ::MAX_LOG_LEVEL);
105 assert_eq!(dirs[2].name, Some("crate2".to_owned()));
106 assert_eq!(dirs[2].level, 4);
107 assert!(filter.is_none());
111 fn parse_logging_spec_invalid_crate() {
112 // test parse_logging_spec with multiple = in specification
113 let (dirs, filter) = parse_logging_spec("crate1::mod1=1=2,crate2=4");
114 assert_eq!(dirs.len(), 1);
115 assert_eq!(dirs[0].name, Some("crate2".to_owned()));
116 assert_eq!(dirs[0].level, 4);
117 assert!(filter.is_none());
121 fn parse_logging_spec_invalid_log_level() {
122 // test parse_logging_spec with 'noNumber' as log level
123 let (dirs, filter) = parse_logging_spec("crate1::mod1=noNumber,crate2=4");
124 assert_eq!(dirs.len(), 1);
125 assert_eq!(dirs[0].name, Some("crate2".to_owned()));
126 assert_eq!(dirs[0].level, 4);
127 assert!(filter.is_none());
131 fn parse_logging_spec_string_log_level() {
132 // test parse_logging_spec with 'warn' as log level
133 let (dirs, filter) = parse_logging_spec("crate1::mod1=wrong,crate2=warn");
134 assert_eq!(dirs.len(), 1);
135 assert_eq!(dirs[0].name, Some("crate2".to_owned()));
136 assert_eq!(dirs[0].level, ::WARN);
137 assert!(filter.is_none());
141 fn parse_logging_spec_empty_log_level() {
142 // test parse_logging_spec with '' as log level
143 let (dirs, filter) = parse_logging_spec("crate1::mod1=wrong,crate2=");
144 assert_eq!(dirs.len(), 1);
145 assert_eq!(dirs[0].name, Some("crate2".to_owned()));
146 assert_eq!(dirs[0].level, ::MAX_LOG_LEVEL);
147 assert!(filter.is_none());
151 fn parse_logging_spec_global() {
152 // test parse_logging_spec with no crate
153 let (dirs, filter) = parse_logging_spec("warn,crate2=4");
154 assert_eq!(dirs.len(), 2);
155 assert_eq!(dirs[0].name, None);
156 assert_eq!(dirs[0].level, 2);
157 assert_eq!(dirs[1].name, Some("crate2".to_owned()));
158 assert_eq!(dirs[1].level, 4);
159 assert!(filter.is_none());
163 fn parse_logging_spec_valid_filter() {
164 let (dirs, filter) = parse_logging_spec("crate1::mod1=1,crate1::mod2,crate2=4/abc");
165 assert_eq!(dirs.len(), 3);
166 assert_eq!(dirs[0].name, Some("crate1::mod1".to_owned()));
167 assert_eq!(dirs[0].level, 1);
169 assert_eq!(dirs[1].name, Some("crate1::mod2".to_owned()));
170 assert_eq!(dirs[1].level, ::MAX_LOG_LEVEL);
172 assert_eq!(dirs[2].name, Some("crate2".to_owned()));
173 assert_eq!(dirs[2].level, 4);
174 assert!(filter.is_some() && filter.unwrap().to_owned() == "abc");
178 fn parse_logging_spec_invalid_crate_filter() {
179 let (dirs, filter) = parse_logging_spec("crate1::mod1=1=2,crate2=4/a.c");
180 assert_eq!(dirs.len(), 1);
181 assert_eq!(dirs[0].name, Some("crate2".to_owned()));
182 assert_eq!(dirs[0].level, 4);
183 assert!(filter.is_some() && filter.unwrap().to_owned() == "a.c");
187 fn parse_logging_spec_empty_with_filter() {
188 let (dirs, filter) = parse_logging_spec("crate1/a*c");
189 assert_eq!(dirs.len(), 1);
190 assert_eq!(dirs[0].name, Some("crate1".to_owned()));
191 assert_eq!(dirs[0].level, ::MAX_LOG_LEVEL);
192 assert!(filter.is_some() && filter.unwrap().to_owned() == "a*c");