Skip to content

Commit 7583544

Browse files
committed
auto merge of #13912 : seanmonstar/rust/logrecord, r=alexcrichton
The logging macros now create a LogRecord, and pass that to the Logger. This will allow custom loggers to change the formatting, and possible filter on more properties of the log record. DefaultLogger's formatting was taken from Python's default formatting: `LEVEL:from: message` Also included: fmt::Arguments now implement Show, so they can be used to extend format strings. @alexcrichton r?
2 parents 600507d + ceb2931 commit 7583544

File tree

6 files changed

+92
-16
lines changed

6 files changed

+92
-16
lines changed

src/liblog/directive.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use std::ascii::StrAsciiExt;
1112
use std::cmp;
1213

1314
#[deriving(Show, Clone)]
@@ -16,13 +17,13 @@ pub struct LogDirective {
1617
pub level: u32,
1718
}
1819

19-
static LOG_LEVEL_NAMES: [&'static str, ..4] = ["error", "warn", "info",
20-
"debug"];
20+
pub static LOG_LEVEL_NAMES: [&'static str, ..4] = ["ERROR", "WARN", "INFO",
21+
"DEBUG"];
2122

2223
/// Parse an individual log level that is either a number or a symbolic log level
2324
fn parse_log_level(level: &str) -> Option<u32> {
2425
from_str::<u32>(level).or_else(|| {
25-
let pos = LOG_LEVEL_NAMES.iter().position(|&name| name == level);
26+
let pos = LOG_LEVEL_NAMES.iter().position(|&name| name.eq_ignore_ascii_case(level));
2627
pos.map(|p| p as u32 + 1)
2728
}).map(|p| cmp::min(p, ::MAX_LOG_LEVEL))
2829
}

src/liblog/lib.rs

+68-8
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ use std::slice;
129129

130130
use sync::one::{Once, ONCE_INIT};
131131

132+
use directive::LOG_LEVEL_NAMES;
133+
132134
pub mod macros;
133135
mod directive;
134136

@@ -162,19 +164,42 @@ local_data_key!(local_logger: ~Logger:Send)
162164
/// can have its own custom logger which can respond to logging messages
163165
/// however it likes.
164166
pub trait Logger {
165-
/// Logs a single message described by the `args` structure. The level is
166-
/// provided in case you want to do things like color the message, etc.
167-
fn log(&mut self, level: u32, args: &fmt::Arguments);
167+
/// Logs a single message described by the `record`.
168+
fn log(&mut self, record: &LogRecord);
168169
}
169170

170171
struct DefaultLogger {
171172
handle: LineBufferedWriter<io::stdio::StdWriter>,
172173
}
173174

175+
/// Wraps the log level with fmt implementations.
176+
#[deriving(Eq, Ord)]
177+
pub struct LogLevel(pub u32);
178+
179+
impl fmt::Show for LogLevel {
180+
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
181+
let LogLevel(level) = *self;
182+
match LOG_LEVEL_NAMES.get(level as uint - 1) {
183+
Some(name) => name.fmt(fmt),
184+
None => level.fmt(fmt)
185+
}
186+
}
187+
}
188+
189+
impl fmt::Signed for LogLevel {
190+
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
191+
let LogLevel(level) = *self;
192+
write!(fmt.buf, "{}", level)
193+
}
194+
}
195+
174196
impl Logger for DefaultLogger {
175-
// by default, just ignore the level
176-
fn log(&mut self, _level: u32, args: &fmt::Arguments) {
177-
match fmt::writeln(&mut self.handle, args) {
197+
fn log(&mut self, record: &LogRecord) {
198+
match write!(&mut self.handle,
199+
"{}:{}: {}",
200+
record.level,
201+
record.module_path,
202+
record.args) {
178203
Err(e) => fail!("failed to log: {}", e),
179204
Ok(()) => {}
180205
}
@@ -198,14 +223,21 @@ impl Drop for DefaultLogger {
198223
///
199224
/// It is not recommended to call this function directly, rather it should be
200225
/// invoked through the logging family of macros.
201-
pub fn log(level: u32, args: &fmt::Arguments) {
226+
#[doc(hidden)]
227+
pub fn log(level: u32, loc: &'static LogLocation, args: &fmt::Arguments) {
202228
// Completely remove the local logger from TLS in case anyone attempts to
203229
// frob the slot while we're doing the logging. This will destroy any logger
204230
// set during logging.
205231
let mut logger = local_data::pop(local_logger).unwrap_or_else(|| {
206232
box DefaultLogger { handle: io::stderr() } as ~Logger:Send
207233
});
208-
logger.log(level, args);
234+
logger.log(&LogRecord {
235+
level: LogLevel(level),
236+
args: args,
237+
file: loc.file,
238+
module_path: loc.module_path,
239+
line: loc.line,
240+
});
209241
local_data::set(local_logger, logger);
210242
}
211243

@@ -223,6 +255,34 @@ pub fn set_logger(logger: ~Logger:Send) -> Option<~Logger:Send> {
223255
return prev;
224256
}
225257

258+
/// A LogRecord is created by the logging macros, and passed as the only
259+
/// argument to Loggers.
260+
#[deriving(Show)]
261+
pub struct LogRecord<'a> {
262+
263+
/// The module path of where the LogRecord originated.
264+
pub module_path: &'a str,
265+
266+
/// The LogLevel of this record.
267+
pub level: LogLevel,
268+
269+
/// The arguments from the log line.
270+
pub args: &'a fmt::Arguments<'a>,
271+
272+
/// The file of where the LogRecord originated.
273+
pub file: &'a str,
274+
275+
/// The line number of where the LogRecord originated.
276+
pub line: uint,
277+
}
278+
279+
#[doc(hidden)]
280+
pub struct LogLocation {
281+
pub module_path: &'static str,
282+
pub file: &'static str,
283+
pub line: uint,
284+
}
285+
226286
/// Tests whether a given module's name is enabled for a particular level of
227287
/// logging. This is the second layer of defense about determining whether a
228288
/// module's log statement should be emitted or not.

src/liblog/macros.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,14 @@
3333
#[macro_export]
3434
macro_rules! log(
3535
($lvl:expr, $($arg:tt)+) => ({
36+
static LOC: ::log::LogLocation = ::log::LogLocation {
37+
line: line!(),
38+
file: file!(),
39+
module_path: module_path!(),
40+
};
3641
let lvl = $lvl;
3742
if log_enabled!(lvl) {
38-
format_args!(|args| { ::log::log(lvl, args) }, $($arg)+)
43+
format_args!(|args| { ::log::log(lvl, &LOC, args) }, $($arg)+)
3944
}
4045
})
4146
)

src/libstd/fmt/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,12 @@ pub struct Arguments<'a> {
565565
args: &'a [Argument<'a>],
566566
}
567567

568+
impl<'a> Show for Arguments<'a> {
569+
fn fmt(&self, fmt: &mut Formatter) -> Result {
570+
write(fmt.buf, self)
571+
}
572+
}
573+
568574
/// When a format is not otherwise specified, types are formatted by ascribing
569575
/// to this trait. There is not an explicit way of selecting this trait to be
570576
/// used for formatting, it is only if no other format is specified.

src/test/run-pass/capturing-logging.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ extern crate native;
1919

2020
use std::fmt;
2121
use std::io::{ChanReader, ChanWriter};
22-
use log::{set_logger, Logger};
22+
use log::{set_logger, Logger, LogRecord};
2323

2424
struct MyWriter(ChanWriter);
2525

2626
impl Logger for MyWriter {
27-
fn log(&mut self, _level: u32, args: &fmt::Arguments) {
27+
fn log(&mut self, record: &LogRecord) {
2828
let MyWriter(ref mut inner) = *self;
29-
fmt::writeln(inner as &mut Writer, args);
29+
fmt::writeln(inner as &mut Writer, record.args);
3030
}
3131
}
3232

src/test/run-pass/ifmt.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ impl fmt::Signed for B {
3333
}
3434
}
3535

36-
macro_rules! t(($a:expr, $b:expr) => { assert_eq!($a, $b.to_owned()) })
36+
macro_rules! t(($a:expr, $b:expr) => { assert_eq!($a.as_slice(), $b) })
3737

3838
pub fn main() {
3939
// Make sure there's a poly formatter that takes anything
@@ -202,6 +202,10 @@ fn test_format_args() {
202202

203203
let s = format_args!(fmt::format, "hello {}", "world");
204204
t!(s, "hello world");
205+
let s = format_args!(|args| {
206+
format!("{}: {}", "args were", args)
207+
}, "hello {}", "world");
208+
t!(s, "args were: hello world");
205209
}
206210

207211
fn test_order() {

0 commit comments

Comments
 (0)