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.
12 use std::ascii::AsciiExt;
15 #[derive(Show, Clone)]
16 pub struct LogDirective {
17 pub name: Option<String>,
21 pub static LOG_LEVEL_NAMES: [&'static str; 4] = ["ERROR", "WARN", "INFO",
24 /// Parse an individual log level that is either a number or a symbolic log level
25 fn parse_log_level(level: &str) -> Option<u32> {
26 level.parse::<u32>().or_else(|| {
27 let pos = LOG_LEVEL_NAMES.iter().position(|&name| name.eq_ignore_ascii_case(level));
28 pos.map(|p| p as u32 + 1)
29 }).map(|p| cmp::min(p, ::MAX_LOG_LEVEL))
32 /// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=1/foo")
33 /// and return a vector with log directives.
35 /// Valid log levels are 0-255, with the most likely ones being 1-4 (defined in
36 /// std::). Also supports string log levels of error, warn, info, and debug
37 pub fn parse_logging_spec(spec: &str) -> (Vec<LogDirective>, Option<Regex>) {
38 let mut dirs = Vec::new();
40 let mut parts = spec.split('/');
41 let mods = parts.next();
42 let filter = parts.next();
43 if parts.next().is_some() {
44 println!("warning: invalid logging spec '{}', \
45 ignoring it (too many '/'s)", spec);
48 mods.map(|m| { for s in m.split(',') {
49 if s.len() == 0 { continue }
50 let mut parts = s.split('=');
51 let (log_level, name) = match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) {
52 (Some(part0), None, None) => {
53 // if the single argument is a log-level string or number,
54 // treat that as a global fallback
55 match parse_log_level(part0) {
56 Some(num) => (num, None),
57 None => (::MAX_LOG_LEVEL, Some(part0)),
60 (Some(part0), Some(""), None) => (::MAX_LOG_LEVEL, Some(part0)),
61 (Some(part0), Some(part1), None) => {
62 match parse_log_level(part1) {
63 Some(num) => (num, Some(part0)),
65 println!("warning: invalid logging spec '{}', \
72 println!("warning: invalid logging spec '{}', \
77 dirs.push(LogDirective {
78 name: name.map(|s| s.to_string()),
83 let filter = filter.map_or(None, |filter| {
84 match Regex::new(filter) {
87 println!("warning: invalid regex filter - {}", e);
93 return (dirs, filter);
98 use super::parse_logging_spec;
101 fn parse_logging_spec_valid() {
102 let (dirs, filter) = parse_logging_spec("crate1::mod1=1,crate1::mod2,crate2=4");
103 assert_eq!(dirs.len(), 3);
104 assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
105 assert_eq!(dirs[0].level, 1);
107 assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
108 assert_eq!(dirs[1].level, ::MAX_LOG_LEVEL);
110 assert_eq!(dirs[2].name, Some("crate2".to_string()));
111 assert_eq!(dirs[2].level, 4);
112 assert!(filter.is_none());
116 fn parse_logging_spec_invalid_crate() {
117 // test parse_logging_spec with multiple = in specification
118 let (dirs, filter) = parse_logging_spec("crate1::mod1=1=2,crate2=4");
119 assert_eq!(dirs.len(), 1);
120 assert_eq!(dirs[0].name, Some("crate2".to_string()));
121 assert_eq!(dirs[0].level, 4);
122 assert!(filter.is_none());
126 fn parse_logging_spec_invalid_log_level() {
127 // test parse_logging_spec with 'noNumber' as log level
128 let (dirs, filter) = parse_logging_spec("crate1::mod1=noNumber,crate2=4");
129 assert_eq!(dirs.len(), 1);
130 assert_eq!(dirs[0].name, Some("crate2".to_string()));
131 assert_eq!(dirs[0].level, 4);
132 assert!(filter.is_none());
136 fn parse_logging_spec_string_log_level() {
137 // test parse_logging_spec with 'warn' as log level
138 let (dirs, filter) = parse_logging_spec("crate1::mod1=wrong,crate2=warn");
139 assert_eq!(dirs.len(), 1);
140 assert_eq!(dirs[0].name, Some("crate2".to_string()));
141 assert_eq!(dirs[0].level, ::WARN);
142 assert!(filter.is_none());
146 fn parse_logging_spec_empty_log_level() {
147 // test parse_logging_spec with '' as log level
148 let (dirs, filter) = parse_logging_spec("crate1::mod1=wrong,crate2=");
149 assert_eq!(dirs.len(), 1);
150 assert_eq!(dirs[0].name, Some("crate2".to_string()));
151 assert_eq!(dirs[0].level, ::MAX_LOG_LEVEL);
152 assert!(filter.is_none());
156 fn parse_logging_spec_global() {
157 // test parse_logging_spec with no crate
158 let (dirs, filter) = parse_logging_spec("warn,crate2=4");
159 assert_eq!(dirs.len(), 2);
160 assert_eq!(dirs[0].name, None);
161 assert_eq!(dirs[0].level, 2);
162 assert_eq!(dirs[1].name, Some("crate2".to_string()));
163 assert_eq!(dirs[1].level, 4);
164 assert!(filter.is_none());
168 fn parse_logging_spec_valid_filter() {
169 let (dirs, filter) = parse_logging_spec("crate1::mod1=1,crate1::mod2,crate2=4/abc");
170 assert_eq!(dirs.len(), 3);
171 assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
172 assert_eq!(dirs[0].level, 1);
174 assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
175 assert_eq!(dirs[1].level, ::MAX_LOG_LEVEL);
177 assert_eq!(dirs[2].name, Some("crate2".to_string()));
178 assert_eq!(dirs[2].level, 4);
179 assert!(filter.is_some() && filter.unwrap().to_string() == "abc");
183 fn parse_logging_spec_invalid_crate_filter() {
184 let (dirs, filter) = parse_logging_spec("crate1::mod1=1=2,crate2=4/a.c");
185 assert_eq!(dirs.len(), 1);
186 assert_eq!(dirs[0].name, Some("crate2".to_string()));
187 assert_eq!(dirs[0].level, 4);
188 assert!(filter.is_some() && filter.unwrap().to_string() == "a.c");
192 fn parse_logging_spec_empty_with_filter() {
193 let (dirs, filter) = parse_logging_spec("crate1/a*c");
194 assert_eq!(dirs.len(), 1);
195 assert_eq!(dirs[0].name, Some("crate1".to_string()));
196 assert_eq!(dirs[0].level, ::MAX_LOG_LEVEL);
197 assert!(filter.is_some() && filter.unwrap().to_string() == "a*c");