diff --git a/Cargo.lock b/Cargo.lock index 137e3d8..00afb42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,7 +77,7 @@ dependencies = [ "env_logger", "log", "tracing", - "tracing-log", + "tracing-core", "tracing-subscriber", ] diff --git a/Cargo.toml b/Cargo.toml index 289c73e..a12f60b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -113,16 +113,33 @@ pre-release-replacements = [ [badges] codecov = { repository = "clap-rs/clap-verbosity-flag" } +[features] +default = ["log"] +log = ["dep:log"] +tracing = ["dep:tracing-core"] + [dependencies] -log = "0.4.1" clap = { version = "4.0.0", default-features = false, features = ["std", "derive"] } +log = { version = "0.4.1", optional = true } +tracing-core = { version = "0.1", optional = true } [dev-dependencies] clap = { version = "4.5.4", default-features = false, features = ["help", "usage"] } env_logger = "0.11.3" tracing = "0.1" tracing-subscriber = "0.3" -tracing-log = "0.2" [lints] workspace = true + +[[example]] +name = "log" +required-features = ["log"] + +[[example]] +name = "log_level" +required-features = ["log"] + +[[example]] +name = "tracing" +required-features = ["tracing"] \ No newline at end of file diff --git a/examples/tracing.rs b/examples/tracing.rs index 6710361..a34246e 100644 --- a/examples/tracing.rs +++ b/examples/tracing.rs @@ -1,6 +1,5 @@ use clap::Parser; use clap_verbosity_flag::Verbosity; -use tracing_log::AsTrace; /// Foo #[derive(Debug, Parser)] @@ -13,7 +12,7 @@ fn main() { let cli = Cli::parse(); tracing_subscriber::fmt() - .with_max_level(cli.verbose.log_level_filter().as_trace()) + .with_max_level(cli.verbose.tracing_level_filter()) .init(); tracing::error!("Engines exploded"); diff --git a/src/lib.rs b/src/lib.rs index 3c1b169..9a5b5f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,7 @@ //! # verbose: Verbosity, //! # } //! let cli = Cli::parse(); +//! # #[cfg(feature = "log")] //! env_logger::Builder::new() //! .filter_level(cli.verbose.log_level_filter()) //! .init(); @@ -63,8 +64,10 @@ use std::fmt; -pub use log::Level; -pub use log::LevelFilter; +#[cfg(feature = "log")] +pub mod log; +#[cfg(feature = "tracing")] +pub mod tracing; /// Logging flags to `#[command(flatten)]` into your CLI #[derive(clap::Args, Debug, Clone, Default)] @@ -111,27 +114,45 @@ impl Verbosity { self.verbose != 0 || self.quiet != 0 } + /// If the user requested complete silence (i.e. not just no-logging). + pub fn is_silent(&self) -> bool { + self.filter() == VerbosityFilter::Off + } + + /// Gets the filter that should be applied to the logger. + pub fn filter(&self) -> VerbosityFilter { + let offset = self.verbose as i16 - self.quiet as i16; + L::default_filter().with_offset(offset) + } +} + +#[cfg(feature = "log")] +impl Verbosity { /// Get the log level. /// /// `None` means all output is disabled. - pub fn log_level(&self) -> Option { + pub fn log_level(&self) -> Option { self.filter().into() } /// Get the log level filter. - pub fn log_level_filter(&self) -> LevelFilter { + pub fn log_level_filter(&self) -> log::LevelFilter { self.filter().into() } +} - /// If the user requested complete silence (i.e. not just no-logging). - pub fn is_silent(&self) -> bool { - self.filter() == VerbosityFilter::Off +#[cfg(feature = "tracing")] +impl Verbosity { + /// Get the tracing level. + /// + /// `None` means all output is disabled. + pub fn tracing_level(&self) -> Option { + self.filter().into() } - /// Gets the filter that should be applied to the logger. - pub fn filter(&self) -> VerbosityFilter { - let offset = self.verbose as i16 - self.quiet as i16; - L::default_filter().with_offset(offset) + /// Get the tracing level filter. + pub fn tracing_level_filter(&self) -> tracing_core::LevelFilter { + self.filter().into() } } @@ -217,58 +238,6 @@ impl fmt::Display for VerbosityFilter { } } -impl From for LevelFilter { - fn from(filter: VerbosityFilter) -> Self { - match filter { - VerbosityFilter::Off => LevelFilter::Off, - VerbosityFilter::Error => LevelFilter::Error, - VerbosityFilter::Warn => LevelFilter::Warn, - VerbosityFilter::Info => LevelFilter::Info, - VerbosityFilter::Debug => LevelFilter::Debug, - VerbosityFilter::Trace => LevelFilter::Trace, - } - } -} - -impl From for VerbosityFilter { - fn from(level: LevelFilter) -> Self { - match level { - LevelFilter::Off => Self::Off, - LevelFilter::Error => Self::Error, - LevelFilter::Warn => Self::Warn, - LevelFilter::Info => Self::Info, - LevelFilter::Debug => Self::Debug, - LevelFilter::Trace => Self::Trace, - } - } -} - -impl From for Option { - fn from(filter: VerbosityFilter) -> Self { - match filter { - VerbosityFilter::Off => None, - VerbosityFilter::Error => Some(Level::Error), - VerbosityFilter::Warn => Some(Level::Warn), - VerbosityFilter::Info => Some(Level::Info), - VerbosityFilter::Debug => Some(Level::Debug), - VerbosityFilter::Trace => Some(Level::Trace), - } - } -} - -impl From> for VerbosityFilter { - fn from(level: Option) -> Self { - match level { - None => Self::Off, - Some(Level::Error) => Self::Error, - Some(Level::Warn) => Self::Warn, - Some(Level::Info) => Self::Info, - Some(Level::Debug) => Self::Debug, - Some(Level::Trace) => Self::Trace, - } - } -} - /// Default to [`VerbosityFilter::Error`] #[derive(Copy, Clone, Debug, Default)] pub struct ErrorLevel; @@ -345,33 +314,6 @@ mod test { Cli::command().debug_assert(); } - #[test] - fn log_level() { - let v = Verbosity::::default(); - assert_eq!(v.log_level(), None); - assert_eq!(v.log_level_filter(), LevelFilter::Off); - - let v = Verbosity::::default(); - assert_eq!(v.log_level(), Some(Level::Error)); - assert_eq!(v.log_level_filter(), LevelFilter::Error); - - let v = Verbosity::::default(); - assert_eq!(v.log_level(), Some(Level::Warn)); - assert_eq!(v.log_level_filter(), LevelFilter::Warn); - - let v = Verbosity::::default(); - assert_eq!(v.log_level(), Some(Level::Info)); - assert_eq!(v.log_level_filter(), LevelFilter::Info); - - let v = Verbosity::::default(); - assert_eq!(v.log_level(), Some(Level::Debug)); - assert_eq!(v.log_level_filter(), LevelFilter::Debug); - - let v = Verbosity::::default(); - assert_eq!(v.log_level(), Some(Level::Trace)); - assert_eq!(v.log_level_filter(), LevelFilter::Trace); - } - /// Asserts that the filter is correct for the given verbosity and quiet values. #[track_caller] fn assert_filter(verbose: u8, quiet: u8, expected: VerbosityFilter) { diff --git a/src/log.rs b/src/log.rs new file mode 100644 index 0000000..94d87d2 --- /dev/null +++ b/src/log.rs @@ -0,0 +1,91 @@ +// These re-exports of the log crate make it easy to use this crate without having to depend on the +// log crate directly. See for more +// information. +pub use log::{Level, LevelFilter}; + +use crate::VerbosityFilter; + +impl From for LevelFilter { + fn from(filter: VerbosityFilter) -> Self { + match filter { + VerbosityFilter::Off => LevelFilter::Off, + VerbosityFilter::Error => LevelFilter::Error, + VerbosityFilter::Warn => LevelFilter::Warn, + VerbosityFilter::Info => LevelFilter::Info, + VerbosityFilter::Debug => LevelFilter::Debug, + VerbosityFilter::Trace => LevelFilter::Trace, + } + } +} + +impl From for VerbosityFilter { + fn from(level: LevelFilter) -> Self { + match level { + LevelFilter::Off => Self::Off, + LevelFilter::Error => Self::Error, + LevelFilter::Warn => Self::Warn, + LevelFilter::Info => Self::Info, + LevelFilter::Debug => Self::Debug, + LevelFilter::Trace => Self::Trace, + } + } +} + +impl From for Option { + fn from(filter: VerbosityFilter) -> Self { + match filter { + VerbosityFilter::Off => None, + VerbosityFilter::Error => Some(Level::Error), + VerbosityFilter::Warn => Some(Level::Warn), + VerbosityFilter::Info => Some(Level::Info), + VerbosityFilter::Debug => Some(Level::Debug), + VerbosityFilter::Trace => Some(Level::Trace), + } + } +} + +impl From> for VerbosityFilter { + fn from(level: Option) -> Self { + match level { + None => Self::Off, + Some(Level::Error) => Self::Error, + Some(Level::Warn) => Self::Warn, + Some(Level::Info) => Self::Info, + Some(Level::Debug) => Self::Debug, + Some(Level::Trace) => Self::Trace, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{DebugLevel, ErrorLevel, InfoLevel, OffLevel, TraceLevel, Verbosity, WarnLevel}; + + #[test] + fn log_level() { + let v = Verbosity::::default(); + assert_eq!(v.log_level(), None); + assert_eq!(v.log_level_filter(), LevelFilter::Off); + + let v = Verbosity::::default(); + assert_eq!(v.log_level(), Some(Level::Error)); + assert_eq!(v.log_level_filter(), LevelFilter::Error); + + let v = Verbosity::::default(); + assert_eq!(v.log_level(), Some(Level::Warn)); + assert_eq!(v.log_level_filter(), LevelFilter::Warn); + + let v = Verbosity::::default(); + assert_eq!(v.log_level(), Some(Level::Info)); + assert_eq!(v.log_level_filter(), LevelFilter::Info); + + let v = Verbosity::::default(); + assert_eq!(v.log_level(), Some(Level::Debug)); + assert_eq!(v.log_level_filter(), LevelFilter::Debug); + + let v = Verbosity::::default(); + assert_eq!(v.log_level(), Some(Level::Trace)); + assert_eq!(v.log_level_filter(), LevelFilter::Trace); + } +} diff --git a/src/tracing.rs b/src/tracing.rs new file mode 100644 index 0000000..3715f52 --- /dev/null +++ b/src/tracing.rs @@ -0,0 +1,91 @@ +// These re-exports of the tracing crate types it easy to use this crate without having to depend on +// the tracing crate directly. See for +// more information. +pub use tracing_core::{Level, LevelFilter}; + +use crate::VerbosityFilter; + +impl From for LevelFilter { + fn from(filter: VerbosityFilter) -> Self { + match filter { + VerbosityFilter::Off => LevelFilter::OFF, + VerbosityFilter::Error => LevelFilter::ERROR, + VerbosityFilter::Warn => LevelFilter::WARN, + VerbosityFilter::Info => LevelFilter::INFO, + VerbosityFilter::Debug => LevelFilter::DEBUG, + VerbosityFilter::Trace => LevelFilter::TRACE, + } + } +} + +impl From for VerbosityFilter { + fn from(level: LevelFilter) -> Self { + match level { + LevelFilter::OFF => Self::Off, + LevelFilter::ERROR => Self::Error, + LevelFilter::WARN => Self::Warn, + LevelFilter::INFO => Self::Info, + LevelFilter::DEBUG => Self::Debug, + LevelFilter::TRACE => Self::Trace, + } + } +} + +impl From for Option { + fn from(filter: VerbosityFilter) -> Self { + match filter { + VerbosityFilter::Off => None, + VerbosityFilter::Error => Some(Level::ERROR), + VerbosityFilter::Warn => Some(Level::WARN), + VerbosityFilter::Info => Some(Level::INFO), + VerbosityFilter::Debug => Some(Level::DEBUG), + VerbosityFilter::Trace => Some(Level::TRACE), + } + } +} + +impl From> for VerbosityFilter { + fn from(level: Option) -> Self { + match level { + None => Self::Off, + Some(Level::ERROR) => Self::Error, + Some(Level::WARN) => Self::Warn, + Some(Level::INFO) => Self::Info, + Some(Level::DEBUG) => Self::Debug, + Some(Level::TRACE) => Self::Trace, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{DebugLevel, ErrorLevel, InfoLevel, OffLevel, TraceLevel, Verbosity, WarnLevel}; + + #[test] + fn tracing_level() { + let v = Verbosity::::default(); + assert_eq!(v.tracing_level(), None); + assert_eq!(v.tracing_level_filter(), LevelFilter::OFF); + + let v = Verbosity::::default(); + assert_eq!(v.tracing_level(), Some(Level::ERROR)); + assert_eq!(v.tracing_level_filter(), LevelFilter::ERROR); + + let v = Verbosity::::default(); + assert_eq!(v.tracing_level(), Some(Level::WARN)); + assert_eq!(v.tracing_level_filter(), LevelFilter::WARN); + + let v = Verbosity::::default(); + assert_eq!(v.tracing_level(), Some(Level::INFO)); + assert_eq!(v.tracing_level_filter(), LevelFilter::INFO); + + let v = Verbosity::::default(); + assert_eq!(v.tracing_level(), Some(Level::DEBUG)); + assert_eq!(v.tracing_level_filter(), LevelFilter::DEBUG); + + let v = Verbosity::::default(); + assert_eq!(v.tracing_level(), Some(Level::TRACE)); + assert_eq!(v.tracing_level_filter(), LevelFilter::TRACE); + } +}