1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
#![doc( html_root_url = "https://docs.rs/log-reroute/0.1.2/log-reroute/", test(attr(deny(warnings))) )] #![deny(missing_docs)] #![forbid(unsafe_code)] //! Crate to reroute logging messages at runtime. //! //! The [`log`](https://crates.io/crates/log) logging facade allows to set only a single //! destination during the whole lifetime of program. If you want to change the logging destination //! multiple times, you can use [`Reroute`](struct.Reroute.html) (either directly, or through the //! [`init`](fn.init.html) and [`reroute`](fn.reroute.html) functions). //! //! This may be useful if you want to log to `stderr` before you know where the main logs will go. //! //! ```rust //! extern crate fern; //! #[macro_use] //! extern crate log; //! extern crate log_reroute; //! extern crate tempfile; //! //! use fern::Dispatch; //! use log::LevelFilter; //! //! fn main() { //! log::set_max_level(LevelFilter::Off); //! info!("This log message goes nowhere"); //! log_reroute::init().unwrap(); //! info!("Still goes nowhere"); //! // Log to stderr //! let early_logger = Dispatch::new().chain(std::io::stderr()).into_log().1; //! log_reroute::reroute_boxed(early_logger); //! info!("This one goes to stderr"); //! // Load file name from config and log to that file //! let file = tempfile::tempfile().unwrap(); //! let logger = Dispatch::new().chain(file).into_log().1; //! log_reroute::reroute_boxed(logger); //! info!("And this one to the file"); //! // Stop logging //! log_reroute::reroute(log_reroute::Dummy); //! } //! ``` extern crate arc_swap; extern crate log; #[macro_use] extern crate once_cell; use std::sync::Arc; use arc_swap::ArcSwap; use log::{Log, Metadata, Record, SetLoggerError}; use once_cell::sync::Lazy; /// A logger that doesn't log. /// /// This is used to stub out the reroute in case no other log is set. pub struct Dummy; impl Log for Dummy { fn enabled(&self, _metadata: &Metadata) -> bool { false } fn log(&self, _record: &Record) {} fn flush(&self) {} } /// A logging proxy. /// /// This logger forwards all calls to currently configured slave logger. /// /// The log routing is implemented in a lock-less and wait-less manner. While not necessarily faster /// than using a mutex (unless there's a lot of contention and the slave logger also doesn't lock), /// it makes it usable in some weird places (like a signal handler). /// /// The rerouting (eg. changing the slave) is lock-less, but may have to wait for current logging /// calls to end and concurrent reroutes will block each other. /// /// # Note /// /// When switching a logger, no care is taken to pair logging calls. In other words, it is possible /// a message is written to the old logger and the new logger is flushed. This shouldn't matter in /// practice, since a logger should flush itself once it is dropped. pub struct Reroute { inner: ArcSwap<Box<Log>>, } impl Reroute { /// Sets a new slave logger. /// /// In case it is already in a box, you should prefer this method over /// [`reroute`](#fn.reroute), since there'll be less indirection. /// /// The old logger (if any) is flushed before dropping. In general, loggers should flush /// themselves on drop, but that may take time. This way we (mostly) ensure the cost of /// flushing is payed here. pub fn reroute_boxed(&self, log: Box<Log>) { let old = self.inner.swap(Arc::new(log)); old.flush(); } /// Sets a new slave logger. pub fn reroute<L: Log + 'static>(&self, log: L) { self.reroute_boxed(Box::new(log)); } /// Stubs out the logger. /// /// Sets the slave logger to one that does nothing (eg. [`Dummy`](struct.Dummy.html)). pub fn clear(&self) { self.reroute(Dummy); } } impl Log for Reroute { fn enabled(&self, metadata: &Metadata) -> bool { self.inner.lease().enabled(metadata) } fn log(&self, record: &Record) { self.inner.lease().log(record) } fn flush(&self) { self.inner.lease().flush() } } impl Default for Reroute { /// Creates a reroute with a [`Dummy`](struct.Dummy.html) slave logger. fn default() -> Self { Self { inner: ArcSwap::from(Arc::new(Box::new(Dummy) as Box<Log>)), } } } /// A global [`Reroute`](struct.Reroute.html) object. /// /// This one is manipulated by the global functions: /// /// * [`init`](fn.init.html) /// * [`reroute`](fn.reroute.html) /// * [`reroute_boxed`](fn.reroute_boxed.html) pub static REROUTE: Lazy<Reroute> = sync_lazy!(Reroute::default()); /// Installs the global [`Reroute`](struct.Reroute.html) instance into the /// [`log`](https://crates.io/crates/log) facade. /// /// Note that the default slave is [`Dummy`](struct.Dummy.html) and you need to call /// [`reroute`](fn.reroute.html) or [`reroute_boxed`](fn.reroute_boxed.html). pub fn init() -> Result<(), SetLoggerError> { log::set_logger(&*REROUTE) } /// Changes the slave of the global [`Reroute`](struct.Reroute.html) instance. /// /// If you have a boxed logger, use [`reroute_boxed`](fn.reroute_boxed.html). pub fn reroute<L: Log + 'static>(log: L) { REROUTE.reroute(log); } /// Changes the slave of the global [`Reroute`](struct.Reroute.html) instance. pub fn reroute_boxed(log: Box<Log>) { REROUTE.reroute_boxed(log) }