Skip to content

Instantly share code, notes, and snippets.

@CAD97
Created April 20, 2018 22:51
Show Gist options
  • Select an option

  • Save CAD97/4c51658d58ae490fb02b6b5642e3bd25 to your computer and use it in GitHub Desktop.

Select an option

Save CAD97/4c51658d58ae490fb02b6b5642e3bd25 to your computer and use it in GitHub Desktop.
I made a lazy slog

This is a potential crate that would simplify the use of the example-lib pattern for slog.

The motivation is the same as that of the example-lib: a library using slog for its logging would like to accept a parent logger if the crate pulling it in is using slog, or fall back to interfacing with log's static logger if not.

At any entry point to your library's interface, you would retrieve the Logger from your lazy slog. After that point, you would pass it around and create child loggers like normal.

This pattern using lazy_static and a DoubleCheckedCell is to support building a library whose ergonomic interface is free functions rather than structs, which can more directly take loggers from their parent logical scope.

The only two issues I see with this approach is a) an extra two pointer chases to get to your Logger and b) what happens if your library is depended on from two locations in the tree? Then you'll get your logger set twice.

The problem is that this is effectively bringing in the idea of a global logger again, but this time bound to your crate rather than a code stack scope. As always, if you're making an internal library and you know the consumer is using slog, just pass the logger around like you do for normal strucured logging.

#![no_std]
#[doc(hidden)]
pub extern crate double_checked_cell;
#[doc(hidden)]
pub extern crate lazy_static;
#[doc(hidden)]
#[cfg_attr(test, macro_use)]
pub extern crate slog;
#[doc(hidden)]
pub extern crate slog_stdlog;
#[doc(hidden)]
pub extern crate std;
#[macro_export]
macro_rules! lazy_slog {
($(#[$meta:meta])* pub(crate) $name:ident: Logger = o!($($o:tt)*);) => {
#[allow(non_camel_case_types)]
#[allow(dead_code)]
#[doc(hidden)]
pub(crate) struct __LazySlog {
cell: $crate::double_checked_cell::DoubleCheckedCell<$crate::slog::Logger>,
}
impl $crate::std::ops::Deref for __LazySlog {
type Target = $crate::slog::Logger;
fn deref(&self) -> &$crate::slog::Logger {
self.cell.get_or_init(|| {
use $crate::slog::Drain;
$crate::slog::Logger::root($crate::slog_stdlog::StdLog.fuse(), o!($($o)*))
})
}
}
/// Initialize this crate's logger with a slog logger of your choosing.
///
/// If you don't call this function to provide a logger,
/// a `slog-stdlog` logger will be used instead,
/// making this crate act as if it were using `log`.
///
/// # Panics
///
/// If the crate logger has been initialized already.
/// This may happen because another function was called before this one,
/// or this function was called twice.
#[allow(unreachable_pub)]
#[allow(dead_code)]
pub fn init_logger(log: &$crate::slog::Logger) {
if let Some(old) = $name.cell.get() {
panic!(
"{}'s logger was already initialized as {:?} when it was set to {:?}",
env!("CARGO_PKG_NAME"), old, log,
);
}
$name.cell.get_or_init(|| log.new(o!($($o)*)));
}
/* lazy-static */
#[allow(missing_copy_implementations)]
#[allow(non_camel_case_types)]
#[allow(dead_code)]
$(#[$meta])*
pub(crate) struct $name;
impl ::std::ops::Deref for $name {
type Target = __LazySlog;
fn deref(&self) -> &__LazySlog {
unsafe {
static mut LAZY: $crate::lazy_static::lazy::Lazy<__LazySlog> =
$crate::lazy_static::lazy::Lazy(::std::ptr::null(), ::std::sync::Once::new());
LAZY.get(|| __LazySlog {
cell: $crate::double_checked_cell::DoubleCheckedCell::new()
})
}
}
}
};
}
#[cfg(test)]
mod tests {
extern crate simple_logger;
use slog::Logger;
lazy_slog! {
pub(crate) LOG: Logger = o!(
"crate" => concat!(env!("CARGO_PKG_NAME"), "@", env!("CARGO_PKG_VERSION"))
);
}
#[test]
fn test() {
simple_logger::init().unwrap();
let log: Logger = LOG.clone();
debug!(log, "This is log");
debug!(LOG, "This is LOG");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment