diff --git a/CHANGELOG.md b/CHANGELOG.md index 6935a3d..b94853b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ## 0.2.0 (TBD) +* Add new `fatal!` macro, which works just like `error!` plus `panic!` (#1) * Add a `std` feature pass-through for the _log_ crate's `std` feature. We use it for tests here. diff --git a/Cargo.lock b/Cargo.lock index 20a5cea..10608a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,10 +1,56 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "autocfg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "cfg-if" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.50" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lock_api" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "log" version = "0.4.6" @@ -13,11 +59,162 @@ dependencies = [ "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "owning_ref" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_os" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_pcg" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scopeguard" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "smallvec" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "stable_deref_trait" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "tao-log" version = "0.2.0" dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -40,7 +237,54 @@ name = "version_check" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "winapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [metadata] +"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "aab692d7759f5cd8c859e169db98ae5b52c924add2af5fbbca11d12fefb567c1" +"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" +"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" +"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" +"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" +"checksum rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3906503e80ac6cbcacb2c2973fa8e473f24d7e2747c8c92bb230c2441cad96b5" +"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" +"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +"checksum rand_os 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f46fbd5550acf75b0c2730f5dd1873751daf9beb8f11b44027778fae50d7feca" +"checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05" +"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c4488ae950c49d403731982257768f48fada354a5203fe81f9bb6f43ca9002be" +"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" +"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 501c460..926e3c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,9 @@ travis-ci = { repository="dekellum/tao-log" } [dependencies] log = { version = ">= 0.4.6, < 0.5" } +[dev-dependencies] +parking_lot = { version = ">= 0.7.1, < 0.8" } + [build-dependencies] version_check = { version=">=0.1.5, <0.2" } @@ -34,3 +37,7 @@ std = [ "log/std" ] [[test]] name = "log_v" harness = false + +[[test]] +name = "fatal" +harness = true diff --git a/src/lib.rs b/src/lib.rs index 7449cfb..424e78c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,10 @@ //! above `debug!` statement does not log, and the cost of formatting the //! message string is avoided. //! +//! To these formatted logging macros, _tao-log_ adds a +//! [`fatal!`](macro.fatal.html) macro, which logs at the _error_ level, and +//! then uses the same message to `panic!`. +//! //! ### Testing for output //! //! The [`log_enabled!`](macro.log_enabled.html) macro may be used to diff --git a/src/macros.rs b/src/macros.rs index b496cb8..d660499 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -7,6 +7,36 @@ // licenses, and is: // Copyright Ⓒ 2015 The Rust Project Developers +/// Log a message at the error level, flush the logger, and then use the same +/// message to panic. +/// +/// This will duplicate the message, once via the registered logger, then +/// again via stderr for the panic (default handler). Since this is a fatal +/// and presumably serious condition, potential duplication is of less concern +/// than the risk of missing the message. This will always `panic!`, even if +/// no logger is configured, or if error level messages aren't logged. +/// +/// # Example +/// +/// ```rust,should_panic +/// # use std::time::{Duration, Instant}; +/// use tao_log::fatal; +/// +/// # let td = Duration::new(0, 100_000_000); +/// fatal!("shields compromised, core breach in {:?}!", td); +/// // ^1 -- error level message: shields compromised, core breach in 100ms! +/// // ^2 -- panic: shields compromised, core breach in 100ms! +/// ``` +#[macro_export] +macro_rules! fatal { + (target: $target:expr, $($arg:tt)+) => ( + $crate::__tao_fatal!($target, $($arg)+) + ); + ($($arg:tt)+) => ( + $crate::__tao_fatal!(module_path!(), $($arg)+) + ); +} + /// Log an expression and its value at any specified level. /// /// Logs with the optional or default (module path of use) target, specified @@ -79,6 +109,21 @@ macro_rules! tracev { ($($arg:tt)+) => ($crate::__tao_logv!($crate::log::Level::Trace, $($arg)+)) } +// Helper macro for `fatal!` +#[doc(hidden)] +#[macro_export] +macro_rules! __tao_fatal { + ($target:expr, $($arg:tt)+) => ( + match format_args!($($arg)+) { + args => { + $crate::error!(target: $target, "{}", args); + $crate::log::logger().flush(); + panic!("{}", args); + } + } + ); +} + // Helper macro for the -v macros, handling the permutations of optional // parameters. Note: The required level parameter is first here for // convenience of internal use with variable-args. diff --git a/test_2015/src/lib.rs b/test_2015/src/lib.rs index 5ff615a..91c116f 100644 --- a/test_2015/src/lib.rs +++ b/test_2015/src/lib.rs @@ -28,3 +28,15 @@ fn test_2015_logv_macros() { warnv!("prefix", "{:?}", v); assert!(errorv!(v)); } + +#[test] +#[should_panic] +fn test_2015_fatal_static_msg() { + fatal!("static fatal msg"); +} + +#[test] +#[should_panic] +fn test_2015_fatal_format_msg() { + fatal!("fmt {}", "failing"); +} diff --git a/test_2018/src/lib.rs b/test_2018/src/lib.rs index 0a1e984..8f3665d 100644 --- a/test_2018/src/lib.rs +++ b/test_2018/src/lib.rs @@ -5,6 +5,7 @@ // Exhaustively list all directly used macros, to test without any other // hidden helper macros in scope. #[cfg(test)] use tao_log::{ + fatal, log, log_enabled, trace, debug, info, warn, error, logv, @@ -34,3 +35,15 @@ fn test_2018_logv_macros() { warnv!("prefix", "{:?}", v); assert!(errorv!(v)); } + +#[test] +#[should_panic] +fn test_2018_fatal_static_msg() { + fatal!("static fatal msg"); +} + +#[test] +#[should_panic] +fn test_2018_fatal_format_msg() { + fatal!("fmt {}", "failing"); +} diff --git a/tests/fatal.rs b/tests/fatal.rs new file mode 100644 index 0000000..8d2727e --- /dev/null +++ b/tests/fatal.rs @@ -0,0 +1,99 @@ +//! Tests of fatal macro + +use std::cell::RefCell; +use std::sync::{Once, Arc}; + +use log::{Log, Record, Metadata}; +#[cfg(feature = "std")] use log::set_boxed_logger; + +use tao_log::fatal; +use parking_lot::{ReentrantMutex, ReentrantMutexGuard}; + +#[cfg(not(feature = "std"))] +fn set_boxed_logger(logger: Box) -> Result<(), log::SetLoggerError> { + log::set_logger(Box::leak(logger)) +} + +struct State { + last_log: ReentrantMutex>>, +} + +impl State { + fn take(&self) -> Option { + self.last_log.lock().replace(None) + } + fn lock(&self) -> ReentrantMutexGuard>> { + self.last_log.lock() + } +} + +struct Logger(Arc); + +impl Log for Logger { + fn enabled(&self, _: &Metadata) -> bool { + true + } + + fn log(&self, record: &Record) { + let msg = format!("{}", record.args()); + self.0.last_log.lock().replace(Some(msg)); + eprintln!("{:5} {}", record.level(), record.args()); + } + + fn flush(&self) { + eprintln!("flushed!"); + } +} + +fn test_logger() -> Arc { + use log::LevelFilter; + static TEST_LOG_INIT: Once = Once::new(); + static mut LOG_STATE: Option> = None; + TEST_LOG_INIT.call_once(|| { + let s = Arc::new(State { + last_log: ReentrantMutex::new(RefCell::new(None)) + }); + unsafe { LOG_STATE = Some(s.clone()); } + set_boxed_logger(Box::new(Logger(s))).unwrap(); + log::set_max_level(LevelFilter::Debug); + }); + unsafe { LOG_STATE.as_ref().unwrap().clone() } +} + +struct StaticMsgCheck(Arc); + +impl Drop for StaticMsgCheck { + fn drop(&mut self) { + eprintln!("checker drop called!"); + assert_eq!(self.0.take(), Some("static fatal msg".to_owned())); + } +} + +#[test] +#[should_panic] +fn fatal_static_msg() { + let s1 = test_logger(); + let s2 = s1.clone(); + let _test_guard = s1.lock(); + let _fr = StaticMsgCheck(s2); + fatal!("static fatal msg"); +} + +struct FormatMsgCheck(Arc); + +impl Drop for FormatMsgCheck { + fn drop(&mut self) { + eprintln!("checker drop called!"); + assert_eq!(self.0.take(), Some("fmt fatal msg".to_owned())); + } +} + +#[test] +#[should_panic] +fn fatal_format_msg() { + let s1 = test_logger(); + let s2 = s1.clone(); + let _test_guard = s1.lock(); + let _fr = FormatMsgCheck(s2); + fatal!(target: "grim", "fmt {} msg", "fatal".to_owned()); +}