Skip to content

Fix #6031 #8906

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions doc/rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -3385,9 +3385,11 @@ The path to a module consists of the crate name, any parent modules,
then the module itself, all separated by double colons (`::`). The
optional log level can be appended to the module path with an equals
sign (`=`) followed by the log level, from 1 to 4, inclusive. Level 1
is the error level, 2 is warning, 3 info, and 4 debug. Any logs
less than or equal to the specified level will be output. If not
specified then log level 4 is assumed.
is the error level, 2 is warning, 3 info, and 4 debug. You can also
use the symbolic constants `error`, `warn`, `info`, and `debug`. Any
logs less than or equal to the specified level will be output. If not
specified then log level 4 is assumed. However, debug messages are
only available if `--cfg=debug` is passed to `rustc`.

As an example, to see all the logs generated by the compiler, you would set
`RUST_LOG` to `rustc`, which is the crate name (as specified in its `link`
Expand Down
191 changes: 145 additions & 46 deletions src/libstd/rt/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use cast::transmute;
use either::*;
use libc::{c_void, uintptr_t, c_char, exit, STDERR_FILENO};
use option::{Some, None};
use option::{Some, None, Option};
use rt::util::dumb_println;
use str::StrSlice;
use str::raw::from_c_str;
Expand All @@ -20,7 +20,7 @@ use vec::ImmutableVector;


struct LogDirective {
name: ~str,
name: Option<~str>,
level: u32
}

Expand All @@ -30,7 +30,6 @@ struct ModEntry{
log_level: *mut u32
}

static MAX_LOG_DIRECTIVES: u32 = 255;
static MAX_LOG_LEVEL: u32 = 255;
static DEFAULT_LOG_LEVEL: u32 = 1;

Expand Down Expand Up @@ -68,42 +67,82 @@ fn iter_crate_map(map: *u8, f: &fn(*mut ModEntry)) {
data: *c_void);
}
}
static log_level_names : &'static[&'static str] = &'static["error", "warn", "info", "debug"];

/// Parse an individual log level that is either a number or a symbolic log level
fn parse_log_level(level: &str) -> Option<u32> {
let num = u32::from_str(level);
let mut log_level;
match num {
Some(num) => {
if num < MAX_LOG_LEVEL {
log_level = Some(num);
} else {
log_level = Some(MAX_LOG_LEVEL);
}
}
_ => {
let position = log_level_names.iter().position(|&name| name == level);
match position {
Some(position) => {
log_level = Some(u32::min(MAX_LOG_LEVEL, (position + 1) as u32))
},
_ => {
log_level = None;
}
}
}
}
log_level
}


/// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=1")
/// and return a vector with log directives.
/// Valid log levels are 0-255, with the most likely ones being 0-3 (defined in std::).
/// Valid log levels are 0-255, with the most likely ones being 1-4 (defined in std::).
/// Also supports string log levels of error, warn, info, and debug

fn parse_logging_spec(spec: ~str) -> ~[LogDirective]{
let mut dirs = ~[];
for s in spec.split_iter(',') {
let parts: ~[&str] = s.split_iter('=').collect();
let mut loglevel;
let mut log_level;
let mut name = Some(parts[0].to_owned());
match parts.len() {
1 => loglevel = MAX_LOG_LEVEL,
2 => {
let num = u32::from_str(parts[1]);
match (num) {
1 => {
//if the single argument is a log-level string or number,
//treat that as a global fallback
let possible_log_level = parse_log_level(parts[0]);
match possible_log_level {
Some(num) => {
if num < MAX_LOG_LEVEL {
loglevel = num;
} else {
loglevel = MAX_LOG_LEVEL;
}
name = None;
log_level = num;
},
_ => {
log_level = MAX_LOG_LEVEL
}
}
}
2 => {
let possible_log_level = parse_log_level(parts[1]);
match possible_log_level {
Some(num) => {
log_level = num;
},
_ => {
dumb_println(fmt!("warning: invalid logging spec \
'%s', ignoring it", s));
loop;
dumb_println(fmt!("warning: invalid logging spec \
'%s', ignoring it", parts[1]));
loop;
}
}
if loglevel > MAX_LOG_LEVEL { loglevel = MAX_LOG_LEVEL}
},
_ => {
dumb_println(fmt!("warning: invalid logging spec '%s',\
ignoring it", s));
loop;
}
}
let dir = LogDirective {name: parts[0].to_owned(), level: loglevel};
let dir = LogDirective {name: name, level: log_level};
dirs.push(dir);
}
return dirs;
Expand All @@ -113,18 +152,30 @@ fn parse_logging_spec(spec: ~str) -> ~[LogDirective]{
/// of log directives
fn update_entry(dirs: &[LogDirective], entry: *mut ModEntry) -> u32 {
let mut new_lvl: u32 = DEFAULT_LOG_LEVEL;
let mut longest_match = 0;
let mut longest_match = -1i;
unsafe {
for dir in dirs.iter() {
let name = from_c_str((*entry).name);
if name.starts_with(dir.name) && dir.name.len() > longest_match {
longest_match = dir.name.len();
new_lvl = dir.level;
}
match dir.name {
None => {
if longest_match == -1 {
longest_match = 0;
new_lvl = dir.level;
}
}
Some(ref dir_name) => {
let name = from_c_str((*entry).name);
let len = dir_name.len() as int;
if name.starts_with(*dir_name) &&
len >= longest_match {
longest_match = len;
new_lvl = dir.level;
}
}
};
}
*(*entry).log_level = new_lvl;
}
if longest_match > 0 { return 1; } else { return 0; }
if longest_match >= 0 { return 1; } else { return 0; }
}

#[fixed_stack_segment] #[inline(never)]
Expand Down Expand Up @@ -238,45 +289,66 @@ extern {
// Tests for parse_logging_spec()
#[test]
fn parse_logging_spec_valid() {
let dirs: ~[LogDirective] = parse_logging_spec(~"crate1::mod1=1,crate1::mod2,crate2=4");
let dirs = parse_logging_spec(~"crate1::mod1=1,crate1::mod2,crate2=4");
assert_eq!(dirs.len(), 3);
assert!(dirs[0].name == ~"crate1::mod1");
assert!(dirs[0].name == Some(~"crate1::mod1"));
assert_eq!(dirs[0].level, 1);

assert!(dirs[1].name == ~"crate1::mod2");
assert!(dirs[1].name == Some(~"crate1::mod2"));
assert_eq!(dirs[1].level, MAX_LOG_LEVEL);

assert!(dirs[2].name == ~"crate2");
assert!(dirs[2].name == Some(~"crate2"));
assert_eq!(dirs[2].level, 4);
}

#[test]
fn parse_logging_spec_invalid_crate() {
// test parse_logging_spec with multiple = in specification
let dirs: ~[LogDirective] = parse_logging_spec(~"crate1::mod1=1=2,crate2=4");
let dirs = parse_logging_spec(~"crate1::mod1=1=2,crate2=4");
assert_eq!(dirs.len(), 1);
assert!(dirs[0].name == ~"crate2");
assert!(dirs[0].name == Some(~"crate2"));
assert_eq!(dirs[0].level, 4);
}

#[test]
fn parse_logging_spec_invalid_log_level() {
// test parse_logging_spec with 'noNumber' as log level
let dirs: ~[LogDirective] = parse_logging_spec(~"crate1::mod1=noNumber,crate2=4");
let dirs = parse_logging_spec(~"crate1::mod1=noNumber,crate2=4");
assert_eq!(dirs.len(), 1);
assert!(dirs[0].name == ~"crate2");
assert!(dirs[0].name == Some(~"crate2"));
assert_eq!(dirs[0].level, 4);
}

#[test]
fn parse_logging_spec_string_log_level() {
// test parse_logging_spec with 'warn' as log level
let dirs = parse_logging_spec(~"crate1::mod1=wrong,crate2=warn");
assert_eq!(dirs.len(), 1);
assert!(dirs[0].name == Some(~"crate2"));
assert_eq!(dirs[0].level, 2);
}

#[test]
fn parse_logging_spec_global() {
// test parse_logging_spec with no crate
let dirs = parse_logging_spec(~"warn,crate2=4");
assert_eq!(dirs.len(), 2);
assert!(dirs[0].name == None);
assert_eq!(dirs[0].level, 2);
assert!(dirs[1].name == Some(~"crate2"));
assert_eq!(dirs[1].level, 4);
}

// Tests for update_entry
#[test]
fn update_entry_match_full_path() {
use c_str::ToCStr;
let dirs = ~[LogDirective {name: ~"crate1::mod1", level: 2 },
LogDirective {name: ~"crate2", level: 3}];
let dirs = ~[LogDirective {name: Some(~"crate1::mod1"), level: 2 },
LogDirective {name: Some(~"crate2"), level: 3}];
let level = &mut 0;
unsafe {
do "crate1::mod1".to_c_str().with_ref |ptr| {
let entry= &ModEntry {name: ptr, log_level: &mut 0};
let entry= &ModEntry {name: ptr, log_level: level};
let m = update_entry(dirs, transmute(entry));
assert!(*entry.log_level == 2);
assert!(m == 1);
Expand All @@ -287,11 +359,12 @@ fn update_entry_match_full_path() {
#[test]
fn update_entry_no_match() {
use c_str::ToCStr;
let dirs = ~[LogDirective {name: ~"crate1::mod1", level: 2 },
LogDirective {name: ~"crate2", level: 3}];
let dirs = ~[LogDirective {name: Some(~"crate1::mod1"), level: 2 },
LogDirective {name: Some(~"crate2"), level: 3}];
let level = &mut 0;
unsafe {
do "crate3::mod1".to_c_str().with_ref |ptr| {
let entry= &ModEntry {name: ptr, log_level: &mut 0};
let entry= &ModEntry {name: ptr, log_level: level};
let m = update_entry(dirs, transmute(entry));
assert!(*entry.log_level == DEFAULT_LOG_LEVEL);
assert!(m == 0);
Expand All @@ -302,11 +375,12 @@ fn update_entry_no_match() {
#[test]
fn update_entry_match_beginning() {
use c_str::ToCStr;
let dirs = ~[LogDirective {name: ~"crate1::mod1", level: 2 },
LogDirective {name: ~"crate2", level: 3}];
let dirs = ~[LogDirective {name: Some(~"crate1::mod1"), level: 2 },
LogDirective {name: Some(~"crate2"), level: 3}];
let level = &mut 0;
unsafe {
do "crate2::mod1".to_c_str().with_ref |ptr| {
let entry= &ModEntry {name: ptr, log_level: &mut 0};
let entry= &ModEntry {name: ptr, log_level: level};
let m = update_entry(dirs, transmute(entry));
assert!(*entry.log_level == 3);
assert!(m == 1);
Expand All @@ -317,14 +391,39 @@ fn update_entry_match_beginning() {
#[test]
fn update_entry_match_beginning_longest_match() {
use c_str::ToCStr;
let dirs = ~[LogDirective {name: ~"crate1::mod1", level: 2 },
LogDirective {name: ~"crate2", level: 3}, LogDirective {name: ~"crate2::mod", level: 4}];
let dirs = ~[LogDirective {name: Some(~"crate1::mod1"), level: 2 },
LogDirective {name: Some(~"crate2"), level: 3},
LogDirective {name: Some(~"crate2::mod"), level: 4}];
let level = &mut 0;
unsafe {
do "crate2::mod1".to_c_str().with_ref |ptr| {
let entry = &ModEntry {name: ptr, log_level: &mut 0};
let entry = &ModEntry {name: ptr, log_level: level};
let m = update_entry(dirs, transmute(entry));
assert!(*entry.log_level == 4);
assert!(m == 1);
}
}
}

#[test]
fn update_entry_match_default() {
use c_str::ToCStr;
let dirs = ~[LogDirective {name: Some(~"crate1::mod1"), level: 2 },
LogDirective {name: None, level: 3}
];
let level = &mut 0;
unsafe {
do "crate1::mod1".to_c_str().with_ref |ptr| {
let entry= &ModEntry {name: ptr, log_level: level};
let m = update_entry(dirs, transmute(entry));
assert!(*entry.log_level == 2);
assert!(m == 1);
}
do "crate2::mod2".to_c_str().with_ref |ptr| {
let entry= &ModEntry {name: ptr, log_level: level};
let m = update_entry(dirs, transmute(entry));
assert!(*entry.log_level == 3);
assert!(m == 1);
}
}
}