// option. This file may not be copied, modified, or distributed
// except according to those terms.
+use std::ascii::StrAsciiExt;
use std::cmp;
#[deriving(Show, Clone)]
pub level: u32,
}
-static LOG_LEVEL_NAMES: [&'static str, ..4] = ["error", "warn", "info",
- "debug"];
+pub static LOG_LEVEL_NAMES: [&'static str, ..4] = ["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> {
from_str::<u32>(level).or_else(|| {
- let pos = LOG_LEVEL_NAMES.iter().position(|&name| name == level);
+ let pos = LOG_LEVEL_NAMES.iter().position(|&name| name.eq_ignore_ascii_case(level));
pos.map(|p| p as u32 + 1)
}).map(|p| cmp::min(p, ::MAX_LOG_LEVEL))
}
use sync::one::{Once, ONCE_INIT};
+use directive::LOG_LEVEL_NAMES;
+
pub mod macros;
mod directive;
/// can have its own custom logger which can respond to logging messages
/// however it likes.
pub trait Logger {
- /// Logs a single message described by the `args` structure. The level is
- /// provided in case you want to do things like color the message, etc.
- fn log(&mut self, level: u32, args: &fmt::Arguments);
+ /// Logs a single message described by the `record`.
+ fn log(&mut self, record: &LogRecord);
}
struct DefaultLogger {
handle: LineBufferedWriter<io::stdio::StdWriter>,
}
+/// Wraps the log level with fmt implementations.
+#[deriving(Eq, Ord)]
+pub struct LogLevel(pub u32);
+
+impl fmt::Show for LogLevel {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let LogLevel(level) = *self;
+ match LOG_LEVEL_NAMES.get(level as uint - 1) {
+ Some(name) => name.fmt(fmt),
+ None => level.fmt(fmt)
+ }
+ }
+}
+
+impl fmt::Signed for LogLevel {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let LogLevel(level) = *self;
+ write!(fmt.buf, "{}", level)
+ }
+}
+
impl Logger for DefaultLogger {
- // by default, just ignore the level
- fn log(&mut self, _level: u32, args: &fmt::Arguments) {
- match fmt::writeln(&mut self.handle, args) {
+ fn log(&mut self, record: &LogRecord) {
+ match write!(&mut self.handle,
+ "{}:{}: {}",
+ record.level,
+ record.module_path,
+ record.args) {
Err(e) => fail!("failed to log: {}", e),
Ok(()) => {}
}
///
/// It is not recommended to call this function directly, rather it should be
/// invoked through the logging family of macros.
-pub fn log(level: u32, args: &fmt::Arguments) {
+#[doc(hidden)]
+pub fn log(level: u32, loc: &'static LogLocation, args: &fmt::Arguments) {
// Completely remove the local logger from TLS in case anyone attempts to
// frob the slot while we're doing the logging. This will destroy any logger
// set during logging.
let mut logger = local_data::pop(local_logger).unwrap_or_else(|| {
~DefaultLogger { handle: io::stderr() } as ~Logger:Send
});
- logger.log(level, args);
+ logger.log(&LogRecord {
+ level: LogLevel(level),
+ args: args,
+ file: loc.file,
+ module_path: loc.module_path,
+ line: loc.line,
+ });
local_data::set(local_logger, logger);
}
return prev;
}
+/// A LogRecord is created by the logging macros, and passed as the only
+/// argument to Loggers.
+#[deriving(Show)]
+pub struct LogRecord<'a> {
+
+ /// The module path of where the LogRecord originated.
+ pub module_path: &'a str,
+
+ /// The LogLevel of this record.
+ pub level: LogLevel,
+
+ /// The arguments from the log line.
+ pub args: &'a fmt::Arguments<'a>,
+
+ /// The file of where the LogRecord originated.
+ pub file: &'a str,
+
+ /// The line number of where the LogRecord originated.
+ pub line: uint,
+}
+
+#[doc(hidden)]
+pub struct LogLocation {
+ pub module_path: &'static str,
+ pub file: &'static str,
+ pub line: uint,
+}
+
/// Tests whether a given module's name is enabled for a particular level of
/// logging. This is the second layer of defense about determining whether a
/// module's log statement should be emitted or not.