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}