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::{slog_crit, slog_debug, slog_error, slog_info, slog_trace, slog_warn};
71
72/// Log a critical level message using current scope logger
73#[macro_export]
74macro_rules! crit( ($($args:tt)+) => {
75    $crate::with_logger(|logger| $crate::slog_crit![logger, $($args)+])
76};);
77/// Log a error level message using current scope logger
78#[macro_export]
79macro_rules! error( ($($args:tt)+) => {
80    $crate::with_logger(|logger| $crate::slog_error![logger, $($args)+])
81};);
82/// Log a warning level message using current scope logger
83#[macro_export]
84macro_rules! warn( ($($args:tt)+) => {
85    $crate::with_logger(|logger| $crate::slog_warn![logger, $($args)+])
86};);
87/// Log a info level message using current scope logger
88#[macro_export]
89macro_rules! info( ($($args:tt)+) => {
90    $crate::with_logger(|logger| $crate::slog_info![logger, $($args)+])
91};);
92/// Log a debug level message using current scope logger
93#[macro_export]
94macro_rules! debug( ($($args:tt)+) => {
95    $crate::with_logger(|logger| $crate::slog_debug![logger, $($args)+])
96};);
97/// Log a trace level message using current scope logger
98#[macro_export]
99macro_rules! trace( ($($args:tt)+) => {
100    $crate::with_logger(|logger| $crate::slog_trace![logger, $($args)+])
101};);
102
103thread_local! {
104    static TL_SCOPES: RefCell<Vec<*const slog::Logger>> = RefCell::new(Vec::with_capacity(8))
105}
106
107lazy_static! {
108    static ref GLOBAL_LOGGER : ArcSwap<slog::Logger> = ArcSwap::from(
109        Arc::new(
110            slog::Logger::root(slog::Discard, o!())
111        )
112    );
113}
114
115struct NoGlobalLoggerSet;
116
117impl slog::Drain for NoGlobalLoggerSet {
118    type Ok = ();
119    type Err = slog::Never;
120
121    fn log(&self,
122           _record: &Record,
123           _values: &OwnedKVList)
124        -> result::Result<Self::Ok, Self::Err> {
125            panic!(
126            "slog-scope: No logger set. Use `slog_scope::set_global_logger` or `slog_scope::scope`."
127            )
128        }
129}
130
131
132/// Guard resetting global logger
133///
134/// On drop it will reset global logger to `slog::Discard`.
135/// This will `drop` any existing global logger.
136#[must_use]
137pub struct GlobalLoggerGuard {
138    canceled : bool,
139}
140
141impl GlobalLoggerGuard {
142    /// Getter for canceled to check status
143    pub fn is_canceled(&self) -> bool {
144        self.canceled
145    }
146
147    fn new() -> Self {
148        GlobalLoggerGuard {
149            canceled: false,
150        }
151    }
152
153    /// Cancel resetting global logger
154    pub fn cancel_reset(mut self) {
155        self.canceled = true;
156    }
157}
158
159impl Drop for GlobalLoggerGuard {
160    fn drop(&mut self) {
161        if !self.canceled {
162            GLOBAL_LOGGER.store(
163                Arc::new(
164                    slog::Logger::root(NoGlobalLoggerSet, o!())
165                    )
166                );
167        }
168    }
169}
170
171
172/// Set global `Logger` that is returned by calls like `logger()` outside of any logging scope.
173pub fn set_global_logger(l: slog::Logger) -> GlobalLoggerGuard {
174    GLOBAL_LOGGER.store(Arc::new(l));
175
176    GlobalLoggerGuard::new()
177}
178
179struct ScopeGuard;
180
181
182impl ScopeGuard {
183    fn new(logger: &slog::Logger) -> Self {
184        TL_SCOPES.with(|s| { s.borrow_mut().push(logger as *const Logger); });
185
186        ScopeGuard
187    }
188}
189
190impl Drop for ScopeGuard {
191    fn drop(&mut self) {
192        TL_SCOPES.with(|s| { s.borrow_mut().pop().expect("TL_SCOPES should contain a logger"); })
193    }
194}
195
196/// Access the `Logger` for the current logging scope
197///
198/// This function needs to clone an underlying scoped
199/// `Logger`. If performance is of vital importance,
200/// use `with_logger`.
201pub fn logger() -> Logger {
202    TL_SCOPES.with(|s| {
203        let s = s.borrow();
204        match s.last() {
205            Some(logger) => (unsafe {&**logger}).clone(),
206            None => Logger::clone(&GLOBAL_LOGGER.load())
207        }
208    })
209}
210
211/// Access the `Logger` for the current logging scope
212///
213/// This function doesn't have to clone the Logger
214/// so it might be a bit faster.
215pub fn with_logger<F, R>(f : F) -> R
216where F : FnOnce(&Logger) -> R {
217    TL_SCOPES.with(|s| {
218        let s = s.borrow();
219        match s.last() {
220            Some(logger) => f(unsafe {&**logger}),
221            None => f(&GLOBAL_LOGGER.load()),
222        }
223    })
224}
225
226/// Execute code in a logging scope
227///
228/// Logging scopes allow using a `slog::Logger` without explicitly
229/// passing it in the code.
230///
231/// At any time current active `Logger` for a given thread can be retrived
232/// with `logger()` call.
233///
234/// Logging scopes can be nested and are panic safe.
235///
236/// `logger` is the `Logger` to use during the duration of `f`.
237/// `with_current_logger` can be used to build it as a child of currently active
238/// logger.
239///
240/// `f` is a code to be executed in the logging scope.
241///
242/// Note: Thread scopes are thread-local. Each newly spawned thread starts
243/// with a global logger, as a current logger.
244pub fn scope<SF, R>(logger: &slog::Logger, f: SF) -> R
245    where SF: FnOnce() -> R
246{
247    let _guard = ScopeGuard::new(&logger);
248    f()
249}