slog_stdlog/
lib.rs

1//! `log` crate adapter for slog-rs
2//!
3//! This crate provides two way compatibility with Rust standard `log` crate.
4//!
5//! ### `log` -> `slog`
6//!
7//! After calling `init()` `slog-stdlog` will take a role of `log` crate
8//! back-end, forwarding all the `log` logging to `slog_scope::logger()`.
9//! In other words, any `log` crate logging statement will behave like it was `slog`
10//! logging statement executed with logger returned by `slog_scope::logger()`.
11//!
12//! See documentation of `slog-scope` for more information about logging scopes.
13//!
14//! See [`init` documentation](fn.init.html) for an example.
15//!
16//! ### `slog` -> `log`
17//!
18//! `StdLog` is `slog::Drain` that will pass all `Record`s passing through it to
19//! `log` crate just like they were crated with `log` crate logging macros in
20//! the first place.
21//!
22//! ## `slog-scope`
23//!
24//! Since `log` does not have any form of context, and does not support `Logger`
25//! `slog-stdlog` relies on "logging scopes" to establish it.
26//!
27//! You must set up logging context for `log` -> `slog` via `slog_scope::scope`
28//! or `slog_scope::set_global_logger`. Setting a global logger upfront via
29//! `slog_scope::set_global_logger` is highly recommended.
30//!
31//! Note: Since `slog-stdlog` v2, unlike previous releases, `slog-stdlog` uses
32//! logging scopes provided by `slog-scope` crate instead of it's own.
33//!
34//! Refer to `slog-scope` crate documentation for more information.
35//!
36//! ### Warning
37//!
38//! Be careful when using both methods at the same time, as a loop can be easily
39//! created: `log` -> `slog` -> `log` -> ...
40//!
41//! ## Compile-time log level filtering
42//!
43//! For filtering `debug!` and other `log` statements at compile-time, configure
44//! the features on the `log` crate in your `Cargo.toml`:
45//!
46//! ```norust
47//! log = { version = "*", features = ["max_level_trace", "release_max_level_warn"] }
48//! ```
49#![warn(missing_docs)]
50
51extern crate log;
52
53#[cfg(feature = "kv_unstable")]
54mod kv;
55
56use slog::{b, Level, KV};
57use std::{fmt, io};
58
59struct Logger;
60
61fn log_to_slog_level(level: log::Level) -> Level {
62    match level {
63        log::Level::Trace => Level::Trace,
64        log::Level::Debug => Level::Debug,
65        log::Level::Info => Level::Info,
66        log::Level::Warn => Level::Warning,
67        log::Level::Error => Level::Error,
68    }
69}
70
71fn record_as_location(r: &log::Record) -> slog::RecordLocation {
72    let module = r.module_path_static().unwrap_or("<unknown>");
73    let file = r.file_static().unwrap_or("<unknown>");
74    let line = r.line().unwrap_or_default();
75
76    slog::RecordLocation {
77        file,
78        line,
79        column: 0,
80        function: "",
81        module,
82    }
83}
84
85impl log::Log for Logger {
86    fn enabled(&self, _: &log::Metadata) -> bool {
87        true
88    }
89
90    fn log(&self, r: &log::Record) {
91        let level = log_to_slog_level(r.metadata().level());
92
93        let args = r.args();
94        let target = r.target();
95        let location = &record_as_location(r);
96        let s = slog::RecordStatic {
97            location,
98            level,
99            tag: target,
100        };
101        #[cfg(feature = "kv_unstable")]
102        {
103            let key_values = r.key_values();
104            let mut visitor = kv::Visitor::new();
105            key_values.visit(&mut visitor).unwrap();
106            slog_scope::with_logger(|logger| logger.log(&slog::Record::new(&s, args, b!(visitor))))
107        }
108        #[cfg(not(feature = "kv_unstable"))]
109        slog_scope::with_logger(|logger| logger.log(&slog::Record::new(&s, args, b!())))
110    }
111
112    fn flush(&self) {}
113}
114
115/// Register `slog-stdlog` as `log` backend.
116///
117/// This will pass all logging statements crated with `log`
118/// crate to current `slog-scope::logger()`.
119///
120/// ```
121/// #[macro_use]
122/// extern crate log;
123/// #[macro_use(slog_o, slog_kv)]
124/// extern crate slog;
125/// extern crate slog_stdlog;
126/// extern crate slog_scope;
127/// extern crate slog_term;
128/// extern crate slog_async;
129///
130/// use slog::Drain;
131///
132/// fn main() {
133///     let decorator = slog_term::TermDecorator::new().build();
134///     let drain = slog_term::FullFormat::new(decorator).build().fuse();
135///     let drain = slog_async::Async::new(drain).build().fuse();
136///     let logger = slog::Logger::root(drain, slog_o!("version" => env!("CARGO_PKG_VERSION")));
137///
138///     let _scope_guard = slog_scope::set_global_logger(logger);
139///     let _log_guard = slog_stdlog::init().unwrap();
140///     // Note: this `info!(...)` macro comes from `log` crate
141///     info!("standard logging redirected to slog");
142/// }
143/// ```
144pub fn init() -> Result<(), log::SetLoggerError> {
145    init_with_level(log::Level::max())
146}
147
148/// Register `slog-stdlog` as `log` backend.
149/// Pass a log::Level to do the log filter explicitly.
150///
151/// This will pass all logging statements crated with `log`
152/// crate to current `slog-scope::logger()`.
153///
154/// ```
155/// #[macro_use]
156/// extern crate log;
157/// #[macro_use(slog_o, slog_kv)]
158/// extern crate slog;
159/// extern crate slog_stdlog;
160/// extern crate slog_scope;
161/// extern crate slog_term;
162/// extern crate slog_async;
163///
164/// use slog::Drain;
165///
166/// fn main() {
167///     let decorator = slog_term::TermDecorator::new().build();
168///     let drain = slog_term::FullFormat::new(decorator).build().fuse();
169///     let drain = slog_async::Async::new(drain).build().fuse();
170///     let logger = slog::Logger::root(drain, slog_o!("version" => env!("CARGO_PKG_VERSION")));
171///
172///     let _scope_guard = slog_scope::set_global_logger(logger);
173///     let _log_guard = slog_stdlog::init_with_level(log::Level::Error).unwrap();
174///     // Note: this `info!(...)` macro comes from `log` crate
175///     info!("standard logging redirected to slog");
176///     error!("standard logging redirected to slog");
177/// }
178/// ```
179pub fn init_with_level(level: log::Level) -> Result<(), log::SetLoggerError> {
180    log::set_boxed_logger(Box::new(Logger))?;
181    log::set_max_level(level.to_level_filter());
182
183    Ok(())
184}
185
186/// Drain logging `Record`s into `log` crate
187///
188/// Any `Record` passing through this `Drain` will be forwarded
189/// to `log` crate, just like it was created with `log` crate macros
190/// in the first place. The message and key-value pairs will be formated
191/// to be one string.
192///
193/// Caution needs to be taken to prevent circular loop where `Logger`
194/// installed via `slog-stdlog::set_logger` would log things to a `StdLog`
195/// drain, which would again log things to the global `Logger` and so on
196/// leading to an infinite recursion.
197pub struct StdLog;
198
199struct LazyLogString<'a> {
200    info: &'a slog::Record<'a>,
201    logger_values: &'a slog::OwnedKVList,
202}
203
204impl<'a> LazyLogString<'a> {
205    fn new(info: &'a slog::Record, logger_values: &'a slog::OwnedKVList) -> Self {
206        LazyLogString {
207            info,
208            logger_values,
209        }
210    }
211}
212
213impl<'a> fmt::Display for LazyLogString<'a> {
214    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
215        write!(f, "{}", self.info.msg())?;
216
217        let io = io::Cursor::new(Vec::new());
218        let mut ser = Ksv::new(io);
219
220        self.logger_values
221            .serialize(self.info, &mut ser)
222            .map_err(|_| fmt::Error)?;
223        self.info
224            .kv()
225            .serialize(self.info, &mut ser)
226            .map_err(|_| fmt::Error)?;
227
228        let values = ser.into_inner().into_inner();
229
230        write!(f, "{}", String::from_utf8_lossy(&values))
231    }
232}
233
234impl slog::Drain for StdLog {
235    type Ok = ();
236    type Err = io::Error;
237
238    fn log(&self, info: &slog::Record, logger_values: &slog::OwnedKVList) -> io::Result<()> {
239        let level = match info.level() {
240            slog::Level::Critical | slog::Level::Error => log::Level::Error,
241            slog::Level::Warning => log::Level::Warn,
242            slog::Level::Info => log::Level::Info,
243            slog::Level::Debug => log::Level::Debug,
244            slog::Level::Trace => log::Level::Trace,
245        };
246
247        let mut target = info.tag();
248        if target.is_empty() {
249            target = info.module();
250        }
251
252        let lazy = LazyLogString::new(info, logger_values);
253        /*
254         * TODO: Support `log` crate key_values here.
255         *
256         * This requires the log/kv_unstable feature here.
257         *
258         * Not supporting this feature is backwards compatible
259         * and it shouldn't break anything (because we've never had),
260         * but is undesirable from a feature-completeness point of view.
261         *
262         * However, this is most likely not as powerful as slog's own
263         * notion of key/value pairs, so I would humbly suggest using `slog`
264         * directly if this feature is important to you ;)
265         *
266         * This avoids using the private log::__private_api_log api function,
267         * which is just a thin wrapper around a `RecordBuilder`.
268         */
269        log::logger().log(
270            &log::Record::builder()
271                .args(format_args!("{}", lazy))
272                .level(level)
273                .target(target)
274                .module_path_static(Some(info.module()))
275                .file_static(Some(info.file()))
276                .line(Some(info.line()))
277                .build(),
278        );
279
280        Ok(())
281    }
282}
283
284/// Key-Separator-Value serializer
285struct Ksv<W: io::Write> {
286    io: W,
287}
288
289impl<W: io::Write> Ksv<W> {
290    fn new(io: W) -> Self {
291        Ksv { io }
292    }
293
294    fn into_inner(self) -> W {
295        self.io
296    }
297}
298
299impl<W: io::Write> slog::Serializer for Ksv<W> {
300    fn emit_arguments(&mut self, key: slog::Key, val: &fmt::Arguments) -> slog::Result {
301        write!(self.io, ", {}: {}", key, val)?;
302        Ok(())
303    }
304}