1use chrono::prelude::*;
20use chrono::Local;
21use slog::Record;
22use slog::{o, Drain, FilterLevel, Fuse, Logger};
23use slog::{FnValue, PushFnValue};
24use slog_async::Async;
25use slog_envlogger::LogBuilder as EnvLogBuilder;
26use slog_json::Json;
27use slog_term::{CompactFormat, TermDecorator};
28use std::env;
29
30pub struct SlogKickstarter {
32 default_filter_level: FilterLevel,
33 debug_modules: Vec<&'static str>,
34 service_name: String,
35 init_std_log: bool,
36 use_json_logging: bool,
37}
38
39impl SlogKickstarter {
40 #[must_use]
41 pub fn new<S: Into<String>>(service_name: S) -> Self {
43 let use_json_logging = env::var("RUST_LOG_JSON")
44 .map(|v| v == "1")
45 .unwrap_or_default();
46
47 Self {
48 default_filter_level: FilterLevel::Info,
49 debug_modules: vec![],
50 service_name: service_name.into(),
51 init_std_log: true,
52 use_json_logging,
53 }
54 }
55
56 pub fn with_debug_log_for(&mut self, module_name: &'static str) -> &mut Self {
59 self.debug_modules.push(module_name);
60 self
61 }
62
63 pub fn with_default_level(&mut self, level: FilterLevel) -> &mut Self {
65 self.default_filter_level = level;
66 self
67 }
68
69 pub fn with_json_logging(&mut self) -> &mut Self {
73 self.use_json_logging = true;
74 self
75 }
76
77 pub fn without_json_logging(&mut self) -> &mut Self {
82 self.use_json_logging = false;
83 self
84 }
85
86 pub fn without_stdlog(&mut self) -> &mut Self {
88 self.init_std_log = false;
89 self
90 }
91
92 #[must_use]
94 pub fn init(&self) -> Logger {
95 let drain = if self.use_json_logging {
97 self.setup_json_logging()
98 } else {
99 self.setup_term_logging()
100 };
101
102 if self.init_std_log {
103 let _guard = slog_stdlog::init();
104 }
105
106 slog::Logger::root(
107 drain,
108 o!("service" => self.service_name.to_owned(), "log_type" => "application", "application_type" => "service", "module" => FnValue(move |info| {
109 info.module().to_string()
110 })
111 ),
112 )
113 }
114
115 fn setup_json_logging(&self) -> Fuse<Async> {
116 let drain = Json::new(std::io::stdout())
117 .add_key_value(o!(
118 "@timestamp" => PushFnValue(move |_ : &Record, ser| {
119 ser.emit(Local::now().to_rfc3339_opts(SecondsFormat::Secs, true))
120 }),
121 "loglevel" => FnValue(move |rinfo : &Record| {
122 rinfo.level().as_str()
123 }),
124 "msg" => PushFnValue(move |record : &Record, ser| {
125 ser.emit(record.msg())
126 }),
127 ))
128 .build()
129 .fuse();
130
131 let builder = EnvLogBuilder::new(drain)
132 .filter(None, self.default_filter_level);
134
135 let builder = self.debug_modules.iter().fold(builder, |b, &module_name| {
136 b.filter(Some(module_name), FilterLevel::Debug)
137 });
138
139 let drain = builder
140 .parse(env::var("RUST_LOG").unwrap_or_default().as_str())
142 .build()
143 .fuse();
144
145 slog_async::Async::new(drain).build().fuse()
146 }
147
148 fn setup_term_logging(&self) -> Fuse<Async> {
149 let decorator = TermDecorator::new().build();
150 let drain = CompactFormat::new(decorator).build().fuse();
151
152 let builder = EnvLogBuilder::new(drain).filter(None, self.default_filter_level);
154
155 let builder = self.debug_modules.iter().fold(builder, |b, &module_name| {
157 b.filter(Some(module_name), FilterLevel::Debug)
158 });
159
160 let drain = builder
161 .parse(env::var("RUST_LOG").unwrap_or_default().as_str())
163 .build()
164 .fuse();
165
166 slog_async::Async::new(drain).build().fuse()
167 }
168}