(index<- ) ./libstd/rt/logging.rs
1 // Copyright 2013 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.
4 //
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.
10
11 use fmt;
12 use from_str::from_str;
13 use libc::exit;
14 use option::{Some, None, Option};
15 use rt::crate_map::{ModEntry, CrateMap, iter_crate_map, get_crate_map};
16 use str::StrSlice;
17 use u32;
18 use vec::ImmutableVector;
19 #[cfg(test)] use cast::transmute;
20
21 struct LogDirective {
22 name: Option<~str>,
23 level: u32
24 }
25
26 static MAX_LOG_LEVEL: u32 = 255;
27 static DEFAULT_LOG_LEVEL: u32 = 1;
28 static log_level_names : &'static[&'static str] = &'static["error", "warn", "info", "debug"];
29
30 /// Parse an individual log level that is either a number or a symbolic log level
31 fn parse_log_level(level: &str) -> Option<u32> {
32 let num = from_str::<u32>(level);
33 let mut log_level;
34 match num {
35 Some(num) => {
36 if num < MAX_LOG_LEVEL {
37 log_level = Some(num);
38 } else {
39 log_level = Some(MAX_LOG_LEVEL);
40 }
41 }
42 _ => {
43 let position = log_level_names.iter().position(|&name| name == level);
44 match position {
45 Some(position) => {
46 log_level = Some(u32::min(MAX_LOG_LEVEL, (position + 1) as u32))
47 },
48 _ => {
49 log_level = None;
50 }
51 }
52 }
53 }
54 log_level
55 }
56
57 /// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=1")
58 /// and return a vector with log directives.
59 /// Valid log levels are 0-255, with the most likely ones being 1-4 (defined in std::).
60 /// Also supports string log levels of error, warn, info, and debug
61 fn parse_logging_spec(spec: ~str) -> ~[LogDirective]{
62 let mut dirs = ~[];
63 for s in spec.split_iter(',') {
64 let parts: ~[&str] = s.split_iter('=').collect();
65 let mut log_level;
66 let mut name = Some(parts[0].to_owned());
67 match parts.len() {
68 1 => {
69 //if the single argument is a log-level string or number,
70 //treat that as a global fallback
71 let possible_log_level = parse_log_level(parts[0]);
72 match possible_log_level {
73 Some(num) => {
74 name = None;
75 log_level = num;
76 },
77 _ => {
78 log_level = MAX_LOG_LEVEL
79 }
80 }
81 }
82 2 => {
83 let possible_log_level = parse_log_level(parts[1]);
84 match possible_log_level {
85 Some(num) => {
86 log_level = num;
87 },
88 _ => {
89 rterrln!("warning: invalid logging spec '{}', \
90 ignoring it", parts[1]);
91 continue
92 }
93 }
94 },
95 _ => {
96 rterrln!("warning: invalid logging spec '{}', \
97 ignoring it", s);
98 continue
99 }
100 }
101 let dir = LogDirective {name: name, level: log_level};
102 dirs.push(dir);
103 }
104 return dirs;
105 }
106
107 /// Set the log level of an entry in the crate map depending on the vector
108 /// of log directives
109 fn update_entry(dirs: &[LogDirective], entry: &ModEntry) -> u32 {
110 let mut new_lvl: u32 = DEFAULT_LOG_LEVEL;
111 let mut longest_match = -1i;
112 for dir in dirs.iter() {
113 match dir.name {
114 None => {
115 if longest_match == -1 {
116 longest_match = 0;
117 new_lvl = dir.level;
118 }
119 }
120 Some(ref dir_name) => {
121 let name = entry.name;
122 let len = dir_name.len() as int;
123 if name.starts_with(*dir_name) &&
124 len >= longest_match {
125 longest_match = len;
126 new_lvl = dir.level;
127 }
128 }
129 };
130 }
131 unsafe { *entry.log_level = new_lvl; }
132 if longest_match >= 0 { return 1; } else { return 0; }
133 }
134
135 #[fixed_stack_segment] #[inline(never)]
136 /// Set log level for every entry in crate_map according to the sepecification
137 /// in settings
138 fn update_log_settings(crate_map: &CrateMap, settings: ~str) {
139 let mut dirs = ~[];
140 if settings.len() > 0 {
141 if settings == ~"::help" || settings == ~"?" {
142 rterrln!("\nCrate log map:\n");
143 do iter_crate_map(crate_map) |entry| {
144 rterrln!(" {}", entry.name);
145 }
146 unsafe { exit(1); }
147 }
148 dirs = parse_logging_spec(settings);
149 }
150
151 let mut n_matches: u32 = 0;
152 do iter_crate_map(crate_map) |entry| {
153 let m = update_entry(dirs, entry);
154 n_matches += m;
155 }
156
157 if n_matches < (dirs.len() as u32) {
158 rterrln!("warning: got {} RUST_LOG specs but only matched\n\
159 {} of them. You may have mistyped a RUST_LOG spec. \n\
160 Use RUST_LOG=::help to see the list of crates and modules.\n",
161 dirs.len(), n_matches);
162 }
163 }
164
165 pub trait Logger {
166 fn log(&mut self, args: &fmt::Arguments);
167 }
168
169 pub struct StdErrLogger;
170
171 impl Logger for StdErrLogger {
172 fn log(&mut self, args: &fmt::Arguments) {
173 // FIXME(#6846): this should not call the blocking version of println,
174 // or at least the default loggers for tasks shouldn't do
175 // that
176 ::rt::util::dumb_println(args);
177 }
178 }
179
180 /// Configure logging by traversing the crate map and setting the
181 /// per-module global logging flags based on the logging spec
182 pub fn init() {
183 use os;
184
185 let log_spec = os::getenv("RUST_LOG");
186 match get_crate_map() {
187 Some(crate_map) => {
188 match log_spec {
189 Some(spec) => {
190 update_log_settings(crate_map, spec);
191 }
192 None => {
193 update_log_settings(crate_map, ~"");
194 }
195 }
196 },
197 _ => {
198 match log_spec {
199 Some(_) => {
200 rterrln!("warning: RUST_LOG set, but no crate map found.");
201 },
202 None => {}
203 }
204 }
205 }
206 }
207
208 // Tests for parse_logging_spec()
209 #[test]
210 fn parse_logging_spec_valid() {
211 let dirs = parse_logging_spec(~"crate1::mod1=1,crate1::mod2,crate2=4");
212 assert_eq!(dirs.len(), 3);
213 assert!(dirs[0].name == Some(~"crate1::mod1"));
214 assert_eq!(dirs[0].level, 1);
215
216 assert!(dirs[1].name == Some(~"crate1::mod2"));
217 assert_eq!(dirs[1].level, MAX_LOG_LEVEL);
218
219 assert!(dirs[2].name == Some(~"crate2"));
220 assert_eq!(dirs[2].level, 4);
221 }
222
223 #[test]
224 fn parse_logging_spec_invalid_crate() {
225 // test parse_logging_spec with multiple = in specification
226 let dirs = parse_logging_spec(~"crate1::mod1=1=2,crate2=4");
227 assert_eq!(dirs.len(), 1);
228 assert!(dirs[0].name == Some(~"crate2"));
229 assert_eq!(dirs[0].level, 4);
230 }
231
232 #[test]
233 fn parse_logging_spec_invalid_log_level() {
234 // test parse_logging_spec with 'noNumber' as log level
235 let dirs = parse_logging_spec(~"crate1::mod1=noNumber,crate2=4");
236 assert_eq!(dirs.len(), 1);
237 assert!(dirs[0].name == Some(~"crate2"));
238 assert_eq!(dirs[0].level, 4);
239 }
240
241 #[test]
242 fn parse_logging_spec_string_log_level() {
243 // test parse_logging_spec with 'warn' as log level
244 let dirs = parse_logging_spec(~"crate1::mod1=wrong,crate2=warn");
245 assert_eq!(dirs.len(), 1);
246 assert!(dirs[0].name == Some(~"crate2"));
247 assert_eq!(dirs[0].level, 2);
248 }
249
250 #[test]
251 fn parse_logging_spec_global() {
252 // test parse_logging_spec with no crate
253 let dirs = parse_logging_spec(~"warn,crate2=4");
254 assert_eq!(dirs.len(), 2);
255 assert!(dirs[0].name == None);
256 assert_eq!(dirs[0].level, 2);
257 assert!(dirs[1].name == Some(~"crate2"));
258 assert_eq!(dirs[1].level, 4);
259 }
260
261 // Tests for update_entry
262 #[test]
263 fn update_entry_match_full_path() {
264 let dirs = ~[LogDirective {name: Some(~"crate1::mod1"), level: 2 },
265 LogDirective {name: Some(~"crate2"), level: 3}];
266 let level = &mut 0;
267 unsafe {
268 let entry= &ModEntry {name:"crate1::mod1", log_level: level};
269 let m = update_entry(dirs, transmute(entry));
270 assert!(*entry.log_level == 2);
271 assert!(m == 1);
272 }
273 }
274
275 #[test]
276 fn update_entry_no_match() {
277 let dirs = ~[LogDirective {name: Some(~"crate1::mod1"), level: 2 },
278 LogDirective {name: Some(~"crate2"), level: 3}];
279 let level = &mut 0;
280 unsafe {
281 let entry= &ModEntry {name: "crate3::mod1", log_level: level};
282 let m = update_entry(dirs, transmute(entry));
283 assert!(*entry.log_level == DEFAULT_LOG_LEVEL);
284 assert!(m == 0);
285 }
286 }
287
288 #[test]
289 fn update_entry_match_beginning() {
290 let dirs = ~[LogDirective {name: Some(~"crate1::mod1"), level: 2 },
291 LogDirective {name: Some(~"crate2"), level: 3}];
292 let level = &mut 0;
293 unsafe {
294 let entry= &ModEntry {name: "crate2::mod1", log_level: level};
295 let m = update_entry(dirs, transmute(entry));
296 assert!(*entry.log_level == 3);
297 assert!(m == 1);
298 }
299 }
300
301 #[test]
302 fn update_entry_match_beginning_longest_match() {
303 let dirs = ~[LogDirective {name: Some(~"crate1::mod1"), level: 2 },
304 LogDirective {name: Some(~"crate2"), level: 3},
305 LogDirective {name: Some(~"crate2::mod"), level: 4}];
306 let level = &mut 0;
307 unsafe {
308 let entry = &ModEntry {name: "crate2::mod1", log_level: level};
309 let m = update_entry(dirs, transmute(entry));
310 assert!(*entry.log_level == 4);
311 assert!(m == 1);
312 }
313 }
314
315 #[test]
316 fn update_entry_match_default() {
317 let dirs = ~[LogDirective {name: Some(~"crate1::mod1"), level: 2 },
318 LogDirective {name: None, level: 3}
319 ];
320 let level = &mut 0;
321 unsafe {
322 let entry= &ModEntry {name: "crate1::mod1", log_level: level};
323 let m = update_entry(dirs, transmute(entry));
324 assert!(*entry.log_level == 2);
325 assert!(m == 1);
326 let entry= &ModEntry {name: "crate2::mod2", log_level: level};
327 let m = update_entry(dirs, transmute(entry));
328 assert!(*entry.log_level == 3);
329 assert!(m == 1);
330 }
331 }
libstd/rt/logging.rs:60:68-60:68 -fn- definition:
/// Also supports string log levels of error, warn, info, and debug
fn parse_logging_spec(spec: ~str) -> ~[LogDirective]{
references:-148: dirs = parse_logging_spec(settings);
libstd/rt/logging.rs:30:82-30:82 -fn- definition:
/// Parse an individual log level that is either a number or a symbolic log level
fn parse_log_level(level: &str) -> Option<u32> {
references:-71: let possible_log_level = parse_log_level(parts[0]);
83: let possible_log_level = parse_log_level(parts[1]);
libstd/rt/logging.rs:20:1-20:1 -struct- definition:
struct LogDirective {
references:-61: fn parse_logging_spec(spec: ~str) -> ~[LogDirective]{
101: let dir = LogDirective {name: name, level: log_level};
109: fn update_entry(dirs: &[LogDirective], entry: &ModEntry) -> u32 {
libstd/rt/logging.rs:137:16-137:16 -fn- definition:
/// in settings
fn update_log_settings(crate_map: &CrateMap, settings: ~str) {
references:-190: update_log_settings(crate_map, spec);
193: update_log_settings(crate_map, ~"");
libstd/rt/logging.rs:108:22-108:22 -fn- definition:
/// of log directives
fn update_entry(dirs: &[LogDirective], entry: &ModEntry) -> u32 {
references:-153: let m = update_entry(dirs, entry);
libstd/rt/logging.rs:164:1-164:1 -trait- definition:
pub trait Logger {
references:-171: impl Logger for StdErrLogger {
libstd/rt/logging.rs:181:62-181:62 -fn- definition:
/// per-module global logging flags based on the logging spec
pub fn init() {
references:-libstd/rt/mod.rs:
236: logging::init();
libstd/rt/logging.rs:168:1-168:1 -struct- definition:
pub struct StdErrLogger;
references:-171: impl Logger for StdErrLogger {
libstd/rt/task.rs:
48: logger: StdErrLogger,