diff --git a/Cargo.lock b/Cargo.lock index 4fa52eef..f92d75c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -76,6 +76,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -157,7 +158,9 @@ dependencies = [ "iq-cli-derive 0.0.0", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "simplelog 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -311,6 +314,16 @@ name = "rustc-demangle" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "simplelog" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "chrono 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "0.14.7" @@ -386,6 +399,16 @@ dependencies = [ "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "time" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ucd-util" version = "0.1.1" @@ -481,6 +504,7 @@ dependencies = [ "checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" +"checksum simplelog 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9cc12b39fdf4c9a07f88bffac2d628f0118ed5ac077a4b0feece61fadf1429e5" "checksum syn 0.14.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e13df71f29f9440b50261a5882c86eac334f1badb3134ec26f0de2f1418e44" "checksum synstructure 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb9b7550d063ea184027c9b8c20ac167cd36d3e06b3a40bceb9d746dc1a7b7" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" @@ -488,6 +512,7 @@ dependencies = [ "checksum termcolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "722426c4a0539da2c4ffd9b419d90ad540b4cff4a053be9069c908d4d07e2836" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" +"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" diff --git a/iq-cli/Cargo.toml b/iq-cli/Cargo.toml index 382a8684..66e58db5 100644 --- a/iq-cli/Cargo.toml +++ b/iq-cli/Cargo.toml @@ -18,14 +18,17 @@ appveyor = { repository = "iqlusioninc/crates" } failure = { version = "0.1", default-features = false, features = ["std"] } iq-cli-derive = { version = "0", path = "../iq-cli-derive" } lazy_static = "1" +log = { version = "0.4", optional = true } +simplelog = { version = "0.5", optional = true } term = "0.5" [dev-dependencies] assert_matches = "1" [features] -default = ["errors", "options", "status"] +default = ["errors", "logging", "options", "status"] errors = [] +logging = ["log", "simplelog"] options = ["iq-cli-derive/options"] status = [] diff --git a/iq-cli/src/lib.rs b/iq-cli/src/lib.rs index 1827dcf0..00f568f2 100644 --- a/iq-cli/src/lib.rs +++ b/iq-cli/src/lib.rs @@ -41,12 +41,18 @@ extern crate failure; extern crate iq_cli_derive; #[macro_use] extern crate lazy_static; +#[cfg(feature = "log")] +pub extern crate log; +#[cfg(feature = "simplelog")] +extern crate simplelog; extern crate term; #[cfg(all(test, feature = "options"))] #[macro_use] extern crate assert_matches; +#[cfg(feature = "simplelog")] +use simplelog::{CombinedLogger, LevelFilter, TermLogger}; pub use term::color::{self, Color}; mod error; @@ -60,3 +66,28 @@ pub use error::Error; #[cfg(feature = "options")] pub use options::Options; pub use shell::{config, status, ColorConfig, Stream}; + +/// Initialize a command-line app with the given options +// TODO: better API for this +#[allow(unused_variables)] +pub fn init(color_config: ColorConfig, verbose: bool) { + config(color_config); + #[cfg(feature = "simplelog")] + init_logging(verbose); +} + +/// Initialize the logging subsystem (i.e. simplelog) +#[cfg(feature = "simplelog")] +fn init_logging(verbose: bool) { + let level_filter = if verbose { + LevelFilter::Debug + } else { + LevelFilter::Info + }; + + let config = simplelog::Config::default(); + + if let Some(logger) = TermLogger::new(level_filter, config) { + CombinedLogger::init(vec![logger]).unwrap() + } +} diff --git a/iq-cli/src/macros/log.rs b/iq-cli/src/macros/log.rs new file mode 100644 index 00000000..61f08f10 --- /dev/null +++ b/iq-cli/src/macros/log.rs @@ -0,0 +1,216 @@ +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// TODO: find a way to re-export the upstream macros rather than copypasta + +//! Logging macros, originally from: +//! + +/// The standard logging macro. +/// +/// This macro will generically log with the specified `Level` and `format!` +/// based argument list. +/// +/// # Examples +/// +/// ```rust +/// # #[macro_use] +/// # extern crate log; +/// use log::Level; +/// +/// # fn main() { +/// let data = (42, "Forty-two"); +/// let private_data = "private"; +/// +/// log!(Level::Error, "Received errors: {}, {}", data.0, data.1); +/// log!(target: "app_events", Level::Warn, "App warning: {}, {}, {}", +/// data.0, data.1, private_data); +/// # } +/// ``` +#[macro_export] +macro_rules! log { + (target: $target:expr, $lvl:expr, $($arg:tt)+) => ({ + let lvl = $lvl; + if lvl <= $crate::log::STATIC_MAX_LEVEL && lvl <= $crate::log::max_level() { + $crate::log::__private_api_log( + format_args!($($arg)+), + lvl, + &($target, module_path!(), file!(), line!()), + ); + } + }); + ($lvl:expr, $($arg:tt)+) => (log!(target: module_path!(), $lvl, $($arg)+)) +} + +/// Logs a message at the error level. +/// +/// # Examples +/// +/// ```rust +/// # #[macro_use] +/// # extern crate log; +/// # fn main() { +/// let (err_info, port) = ("No connection", 22); +/// +/// error!("Error: {} on port {}", err_info, port); +/// error!(target: "app_events", "App Error: {}, Port: {}", err_info, 22); +/// # } +/// ``` +#[macro_export] +macro_rules! error { + (target: $target:expr, $($arg:tt)*) => ( + log!(target: $target, $crate::log::Level::Error, $($arg)*); + ); + ($($arg:tt)*) => ( + log!($crate::log::Level::Error, $($arg)*); + ) +} + +/// Logs a message at the warn level. +/// +/// # Examples +/// +/// ```rust +/// # #[macro_use] +/// # extern crate log; +/// # fn main() { +/// let warn_description = "Invalid Input"; +/// +/// warn!("Warning! {}!", warn_description); +/// warn!(target: "input_events", "App received warning: {}", warn_description); +/// # } +/// ``` +#[macro_export] +macro_rules! warn { + (target: $target:expr, $($arg:tt)*) => ( + log!(target: $target, $crate::log::Level::Warn, $($arg)*); + ); + ($($arg:tt)*) => ( + log!($crate::log::Level::Warn, $($arg)*); + ) +} + +/// Logs a message at the info level. +/// +/// # Examples +/// +/// ```rust +/// # #[macro_use] +/// # extern crate log; +/// # fn main() { +/// # struct Connection { port: u32, speed: f32 } +/// let conn_info = Connection { port: 40, speed: 3.20 }; +/// +/// info!("Connected to port {} at {} Mb/s", conn_info.port, conn_info.speed); +/// info!(target: "connection_events", "Successfull connection, port: {}, speed: {}", +/// conn_info.port, conn_info.speed); +/// # } +/// ``` +#[macro_export] +macro_rules! info { + (target: $target:expr, $($arg:tt)*) => ( + log!(target: $target, $crate::log::Level::Info, $($arg)*); + ); + ($($arg:tt)*) => ( + log!($crate::log::Level::Info, $($arg)*); + ) +} + +/// Logs a message at the debug level. +/// +/// # Examples +/// +/// ```rust +/// # #[macro_use] +/// # extern crate log; +/// # fn main() { +/// # struct Position { x: f32, y: f32 } +/// let pos = Position { x: 3.234, y: -1.223 }; +/// +/// debug!("New position: x: {}, y: {}", pos.x, pos.y); +/// debug!(target: "app_events", "New position: x: {}, y: {}", pos.x, pos.y); +/// # } +/// ``` +#[macro_export] +macro_rules! debug { + (target: $target:expr, $($arg:tt)*) => ( + log!(target: $target, $crate::log::Level::Debug, $($arg)*); + ); + ($($arg:tt)*) => ( + log!($crate::log::Level::Debug, $($arg)*); + ) +} + +/// Logs a message at the trace level. +/// +/// # Examples +/// +/// ```rust +/// # #[macro_use] +/// # extern crate log; +/// # fn main() { +/// # struct Position { x: f32, y: f32 } +/// let pos = Position { x: 3.234, y: -1.223 }; +/// +/// trace!("Position is: x: {}, y: {}", pos.x, pos.y); +/// trace!(target: "app_events", "x is {} and y is {}", +/// if pos.x >= 0.0 { "positive" } else { "negative" }, +/// if pos.y >= 0.0 { "positive" } else { "negative" }); +/// # } +/// ``` +#[macro_export] +macro_rules! trace { + (target: $target:expr, $($arg:tt)*) => ( + log!(target: $target, $crate::log::Level::Trace, $($arg)*); + ); + ($($arg:tt)*) => ( + log!($crate::log::Level::Trace, $($arg)*); + ) +} + +/// Determines if a message logged at the specified level in that module will +/// be logged. +/// +/// This can be used to avoid expensive computation of log message arguments if +/// the message would be ignored anyway. +/// +/// # Examples +/// +/// ```rust +/// # #[macro_use] +/// # extern crate log; +/// use log::Level::Debug; +/// +/// # fn foo() { +/// if log_enabled!(Debug) { +/// let data = expensive_call(); +/// debug!("expensive debug data: {} {}", data.x, data.y); +/// } +/// if log_enabled!(target: "Global", Debug) { +/// let data = expensive_call(); +/// debug!(target: "Global", "expensive debug data: {} {}", data.x, data.y); +/// } +/// # } +/// # struct Data { x: u32, y: u32 } +/// # fn expensive_call() -> Data { Data { x: 0, y: 0 } } +/// # fn main() {} +/// ``` +#[macro_export] +macro_rules! log_enabled { + (target: $target:expr, $lvl:expr) => {{ + let lvl = $lvl; + lvl <= $crate::log::STATIC_MAX_LEVEL + && lvl <= $crate::log::max_level() + && $crate::log::__private_api_enabled(lvl, $target) + }}; + ($lvl:expr) => { + log_enabled!(target: module_path!(), $lvl) + }; +} diff --git a/iq-cli/src/macros/mod.rs b/iq-cli/src/macros/mod.rs index ceac84ef..8f1c2e4b 100644 --- a/iq-cli/src/macros/mod.rs +++ b/iq-cli/src/macros/mod.rs @@ -3,5 +3,8 @@ #[cfg(feature = "errors")] pub mod error; +#[cfg(feature = "log")] +pub mod log; + #[cfg(feature = "status")] pub mod status;