use chrono::prelude::*;
use chrono::Local;
use slog::Record;
use slog::{o, Drain, FilterLevel, Fuse, Logger};
use slog::{FnValue, PushFnValue};
use slog_async::Async;
use slog_envlogger::LogBuilder as EnvLogBuilder;
use slog_json::Json;
use slog_term::{CompactFormat, TermDecorator};
use std::env;
pub struct SlogKickstarter {
default_filter_level: FilterLevel,
debug_modules: Vec<&'static str>,
service_name: String,
init_std_log: bool,
use_json_logging: bool,
}
impl SlogKickstarter {
#[must_use]
pub fn new<S: Into<String>>(service_name: S) -> Self {
let use_json_logging = env::var("RUST_LOG_JSON")
.map(|v| v == "1")
.unwrap_or_default();
Self {
default_filter_level: FilterLevel::Info,
debug_modules: vec![],
service_name: service_name.into(),
init_std_log: true,
use_json_logging,
}
}
pub fn with_debug_log_for(&mut self, module_name: &'static str) -> &mut Self {
self.debug_modules.push(module_name);
self
}
pub fn with_default_level(&mut self, level: FilterLevel) -> &mut Self {
self.default_filter_level = level;
self
}
pub fn with_json_logging(&mut self) -> &mut Self {
self.use_json_logging = true;
self
}
pub fn without_json_logging(&mut self) -> &mut Self {
self.use_json_logging = false;
self
}
pub fn without_stdlog(&mut self) -> &mut Self {
self.init_std_log = false;
self
}
#[must_use]
pub fn init(&self) -> Logger {
let drain = if self.use_json_logging {
self.setup_json_logging()
} else {
self.setup_term_logging()
};
if self.init_std_log {
let _guard = slog_stdlog::init();
}
slog::Logger::root(
drain,
o!("service" => self.service_name.to_owned(), "log_type" => "application", "application_type" => "service", "module" => FnValue(move |info| {
info.module().to_string()
})
),
)
}
fn setup_json_logging(&self) -> Fuse<Async> {
let drain = Json::new(std::io::stdout())
.add_key_value(o!(
"@timestamp" => PushFnValue(move |_ : &Record, ser| {
ser.emit(Local::now().to_rfc3339_opts(SecondsFormat::Secs, true))
}),
"loglevel" => FnValue(move |rinfo : &Record| {
rinfo.level().as_str()
}),
"msg" => PushFnValue(move |record : &Record, ser| {
ser.emit(record.msg())
}),
))
.build()
.fuse();
let builder = EnvLogBuilder::new(drain)
.filter(None, self.default_filter_level);
let builder = self.debug_modules.iter().fold(builder, |b, &module_name| {
b.filter(Some(module_name), FilterLevel::Debug)
});
let drain = builder
.parse(env::var("RUST_LOG").unwrap_or_default().as_str())
.build()
.fuse();
slog_async::Async::new(drain).build().fuse()
}
fn setup_term_logging(&self) -> Fuse<Async> {
let decorator = TermDecorator::new().build();
let drain = CompactFormat::new(decorator).build().fuse();
let builder = EnvLogBuilder::new(drain).filter(None, self.default_filter_level);
let builder = self.debug_modules.iter().fold(builder, |b, &module_name| {
b.filter(Some(module_name), FilterLevel::Debug)
});
let drain = builder
.parse(env::var("RUST_LOG").unwrap_or_default().as_str())
.build()
.fuse();
slog_async::Async::new(drain).build().fuse()
}
}