use log;
use protocol::{Breadcrumb, Event, Exception, Level};
use api::add_breadcrumb;
use backtrace_support::current_stacktrace;
use scope::with_client_and_scope;
pub struct LoggerOptions {
pub global_filter: Option<log::LevelFilter>,
pub filter: log::LevelFilter,
pub emit_breadcrumbs: bool,
pub emit_error_events: bool,
}
impl Default for LoggerOptions {
fn default() -> LoggerOptions {
LoggerOptions {
global_filter: None,
filter: log::LevelFilter::Info,
emit_breadcrumbs: true,
emit_error_events: true,
}
}
}
pub struct Logger {
dest: Option<Box<log::Log>>,
options: LoggerOptions,
}
impl Logger {
pub fn new(dest: Option<Box<log::Log>>, options: LoggerOptions) -> Logger {
Logger { dest, options }
}
pub fn options(&self) -> &LoggerOptions {
&self.options
}
pub fn dest_log(&self) -> Option<&log::Log> {
self.dest.as_ref().map(|x| &**x)
}
}
pub fn breadcrumb_from_record(record: &log::Record) -> Breadcrumb {
Breadcrumb {
ty: "log".into(),
level: convert_log_level(record.level()),
category: Some(record.target().into()),
message: Some(format!("{}", record.args())),
..Default::default()
}
}
pub fn event_from_record(record: &log::Record, with_stacktrace: bool) -> Event<'static> {
Event {
logger: Some(record.target().into()),
level: convert_log_level(record.level()),
exceptions: vec![
Exception {
ty: record.target().into(),
value: Some(format!("{}", record.args())),
stacktrace: if with_stacktrace {
current_stacktrace()
} else {
None
},
..Default::default()
},
],
..Default::default()
}
}
impl log::Log for Logger {
fn enabled(&self, md: &log::Metadata) -> bool {
if let Some(global_filter) = self.options.global_filter {
if md.level() < global_filter {
return false;
}
}
md.level() <= self.options.filter || self.dest.as_ref().map_or(false, |x| x.enabled(md))
}
fn log(&self, record: &log::Record) {
if self.options.emit_error_events && record.level() <= log::Level::Error {
with_client_and_scope(|client, scope| {
client.capture_event(event_from_record(record, true), Some(scope))
});
}
if record.level() <= self.options.filter {
add_breadcrumb(|| breadcrumb_from_record(record))
}
if let Some(ref log) = self.dest {
if log.enabled(record.metadata()) {
log.log(record);
}
}
}
fn flush(&self) {
if let Some(ref log) = self.dest {
log.flush();
}
}
}
fn convert_log_level(level: log::Level) -> Level {
match level {
log::Level::Error => Level::Error,
log::Level::Warn => Level::Warning,
log::Level::Info => Level::Info,
log::Level::Debug | log::Level::Trace => Level::Debug,
}
}
pub fn init(dest: Option<Box<log::Log>>, options: LoggerOptions) {
let logger = Logger::new(dest, options);
if let Some(filter) = logger.options().global_filter {
log::set_max_level(filter);
} else {
log::set_max_level(logger.options().filter);
}
log::set_boxed_logger(Box::new(logger)).unwrap();
}