slog_scope/
lib.rs

1//! Logging scopes for slog-rs
2//!
3//! Logging scopes are convenience functionality for slog-rs to free user from manually passing
4//! `Logger` objects around.
5//!
6//! Set of macros is also provided as an alternative to original `slog` crate macros, for logging
7//! directly to `Logger` of the current logging scope.
8//!
9//! # Set global logger upfront
10//!
11//! **Warning**: Since `slog-scope` version 4.0.0, `slog-scope` defaults to
12//! panicking on logging if no scope or global logger was set. Because of it, it
13//! is advised to always set a global logger upfront with `set_global_logger`.
14//!
15//! # Using `slog-scope` as a part of API is not advised
16//!
17//! Part of a `slog` logging philosophy is ability to freely express logging contexts
18//! according to logical structure, rather than callstack structure. By using
19//! logging scopes the logging context is tied to code flow again, which is less
20//! expressive.
21//!
22//! It is generally advised **NOT** to use `slog_scope` in libraries. Read more in
23//! [slog-rs FAQ](https://github.com/slog-rs/slog/wiki/FAQ#do-i-have-to-pass-logger-around)
24//!
25//! ```
26//! #[macro_use(slog_o, slog_info, slog_log, slog_record, slog_record_static, slog_b, slog_kv)]
27//! extern crate slog;
28//! #[macro_use]
29//! extern crate slog_scope;
30//! extern crate slog_term;
31//!
32//! use slog::Drain;
33//!
34//! fn foo() {
35//!     slog_info!(slog_scope::logger(), "foo");
36//!     info!("foo"); // Same as above, but more ergonomic and a bit faster
37//!                   // since it uses `with_logger`
38//! }
39//!
40//! fn main() {
41//!     let plain = slog_term::PlainSyncDecorator::new(std::io::stdout());
42//!     let log = slog::Logger::root(
43//!         slog_term::FullFormat::new(plain)
44//!         .build().fuse(), slog_o!()
45//!     );
46//!
47//!     // Make sure to save the guard, see documentation for more information
48//!     let _guard = slog_scope::set_global_logger(log);
49//!     slog_scope::scope(&slog_scope::logger().new(slog_o!("scope" => "1")),
50//!         || foo()
51//!     );
52//! }
53
54#![warn(missing_docs)]
55
56#[macro_use(o)]
57extern crate slog;
58#[macro_use]
59extern crate lazy_static;
60extern crate arc_swap;
61
62use slog::{Logger, Record, OwnedKVList};
63
64use std::sync::Arc;
65use std::cell::RefCell;
66use arc_swap::ArcSwap;
67
68use std::result;
69
70pub use slog::{
71    crit as slog_crit, debug as slog_debug, error as slog_error, info as slog_info,
72    trace as slog_trace, warn as slog_warn,
73};
74
75/// Log a critical level message using current scope logger
76#[macro_export]
77macro_rules! crit( ($($args:tt)+) => {
78    $crate::with_logger(|logger| $crate::slog_crit![logger, $($args)+])
79};);
80/// Log a error level message using current scope logger
81#[macro_export]
82macro_rules! error( ($($args:tt)+) => {
83    $crate::with_logger(|logger| $crate::slog_error![logger, $($args)+])
84};);
85/// Log a warning level message using current scope logger
86#[macro_export]
87macro_rules! warn( ($($args:tt)+) => {
88    $crate::with_logger(|logger| $crate::slog_warn![logger, $($args)+])
89};);
90/// Log a info level message using current scope logger
91#[macro_export]
92macro_rules! info( ($($args:tt)+) => {
93    $crate::with_logger(|logger| $crate::slog_info![logger, $($args)+])
94};);
95/// Log a debug level message using current scope logger
96#[macro_export]
97macro_rules! debug( ($($args:tt)+) => {
98    $crate::with_logger(|logger| $crate::slog_debug![logger, $($args)+])
99};);
100/// Log a trace level message using current scope logger
101#[macro_export]
102macro_rules! trace( ($($args:tt)+) => {
103    $crate::with_logger(|logger| $crate::slog_trace![logger, $($args)+])
104};);
105
106thread_local! {
107    static TL_SCOPES: RefCell<Vec<*const slog::Logger>> = RefCell::new(Vec::with_capacity(8))
108}
109
110lazy_static! {
111    static ref GLOBAL_LOGGER : ArcSwap<slog::Logger> = ArcSwap::from(
112        Arc::new(
113            slog::Logger::root(slog::Discard, o!())
114        )
115    );
116}
117
118struct NoGlobalLoggerSet;
119
120impl slog::Drain for NoGlobalLoggerSet {
121    type Ok = ();
122    type Err = slog::Never;
123
124    fn log(&self,
125           _record: &Record,
126           _values: &OwnedKVList)
127        -> result::Result<Self::Ok, Self::Err> {
128            panic!(
129            "slog-scope: No logger set. Use `slog_scope::set_global_logger` or `slog_scope::scope`."
130            )
131        }
132}
133
134
135/// Guard resetting global logger
136///
137/// On drop it will reset global logger to an `slog::Drain` that panics upon receiving a log record,
138/// so use outside of main is discouraged.
139/// This will `drop` any existing global logger.
140#[must_use]
141pub struct GlobalLoggerGuard {
142    canceled : bool,
143}
144
145impl GlobalLoggerGuard {
146    /// Getter for canceled to check status
147    pub fn is_canceled(&self) -> bool {
148        self.canceled
149    }
150
151    fn new() -> Self {
152        GlobalLoggerGuard {
153            canceled: false,
154        }
155    }
156
157    /// Cancel resetting global logger
158    pub fn cancel_reset(mut self) {
159        self.canceled = true;
160    }
161}
162
163impl Drop for GlobalLoggerGuard {
164    fn drop(&mut self) {
165        if !self.canceled {
166            GLOBAL_LOGGER.store(
167                Arc::new(
168                    slog::Logger::root(NoGlobalLoggerSet, o!())
169                    )
170                );
171        }
172    }
173}
174
175
176/// Set global `Logger` that is returned by calls like `logger()` outside of any logging scope.
177pub fn set_global_logger(l: slog::Logger) -> GlobalLoggerGuard {
178    GLOBAL_LOGGER.store(Arc::new(l));
179
180    GlobalLoggerGuard::new()
181}
182
183struct ScopeGuard;
184
185
186impl ScopeGuard {
187    fn new(logger: &slog::Logger) -> Self {
188        TL_SCOPES.with(|s| { s.borrow_mut().push(logger as *const Logger); });
189
190        ScopeGuard
191    }
192}
193
194impl Drop for ScopeGuard {
195    fn drop(&mut self) {
196        TL_SCOPES.with(|s| { s.borrow_mut().pop().expect("TL_SCOPES should contain a logger"); })
197    }
198}
199
200/// Access the `Logger` for the current logging scope
201///
202/// This function needs to clone an underlying scoped
203/// `Logger`. If performance is of vital importance,
204/// use `with_logger`.
205pub fn logger() -> Logger {
206    TL_SCOPES.with(|s| {
207        let s = s.borrow();
208        match s.last() {
209            Some(logger) => (unsafe {&**logger}).clone(),
210            None => Logger::clone(&GLOBAL_LOGGER.load())
211        }
212    })
213}
214
215/// Access the `Logger` for the current logging scope
216///
217/// This function doesn't have to clone the Logger
218/// so it might be a bit faster.
219pub fn with_logger<F, R>(f : F) -> R
220where F : FnOnce(&Logger) -> R {
221    TL_SCOPES.with(|s| {
222        let s = s.borrow();
223        match s.last() {
224            Some(logger) => f(unsafe {&**logger}),
225            None => f(&GLOBAL_LOGGER.load()),
226        }
227    })
228}
229
230/// Execute code in a logging scope
231///
232/// Logging scopes allow using a `slog::Logger` without explicitly
233/// passing it in the code.
234///
235/// At any time current active `Logger` for a given thread can be retrived
236/// with `logger()` call.
237///
238/// Logging scopes can be nested and are panic safe.
239///
240/// `logger` is the `Logger` to use during the duration of `f`.
241/// `with_current_logger` can be used to build it as a child of currently active
242/// logger.
243///
244/// `f` is a code to be executed in the logging scope.
245///
246/// Note: Thread scopes are thread-local. Each newly spawned thread starts
247/// with a global logger, as a current logger.
248pub fn scope<SF, R>(logger: &slog::Logger, f: SF) -> R
249    where SF: FnOnce() -> R
250{
251    let _guard = ScopeGuard::new(logger);
252    f()
253}