Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for tracing crate #127

Merged
merged 2 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 19 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
3 changes: 1 addition & 2 deletions examples/tracing.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use clap::Parser;
use clap_verbosity_flag::Verbosity;
use tracing_log::AsTrace;

/// Foo
#[derive(Debug, Parser)]
Expand All @@ -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");
Expand Down
122 changes: 32 additions & 90 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -111,27 +114,45 @@ impl<L: LogLevel> Verbosity<L> {
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<L: LogLevel> Verbosity<L> {
/// Get the log level.
///
/// `None` means all output is disabled.
pub fn log_level(&self) -> Option<Level> {
pub fn log_level(&self) -> Option<log::Level> {
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<L: LogLevel> Verbosity<L> {
/// Get the tracing level.
///
/// `None` means all output is disabled.
pub fn tracing_level(&self) -> Option<tracing_core::Level> {
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()
}
}

Expand Down Expand Up @@ -217,58 +238,6 @@ impl fmt::Display for VerbosityFilter {
}
}

impl From<VerbosityFilter> 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<LevelFilter> 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<VerbosityFilter> for Option<Level> {
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<Option<Level>> for VerbosityFilter {
fn from(level: Option<Level>) -> 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;
Expand Down Expand Up @@ -345,33 +314,6 @@ mod test {
Cli::command().debug_assert();
}

#[test]
fn log_level() {
let v = Verbosity::<OffLevel>::default();
assert_eq!(v.log_level(), None);
assert_eq!(v.log_level_filter(), LevelFilter::Off);

let v = Verbosity::<ErrorLevel>::default();
assert_eq!(v.log_level(), Some(Level::Error));
assert_eq!(v.log_level_filter(), LevelFilter::Error);

let v = Verbosity::<WarnLevel>::default();
assert_eq!(v.log_level(), Some(Level::Warn));
assert_eq!(v.log_level_filter(), LevelFilter::Warn);

let v = Verbosity::<InfoLevel>::default();
assert_eq!(v.log_level(), Some(Level::Info));
assert_eq!(v.log_level_filter(), LevelFilter::Info);

let v = Verbosity::<DebugLevel>::default();
assert_eq!(v.log_level(), Some(Level::Debug));
assert_eq!(v.log_level_filter(), LevelFilter::Debug);

let v = Verbosity::<TraceLevel>::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<L: LogLevel>(verbose: u8, quiet: u8, expected: VerbosityFilter) {
Expand Down
91 changes: 91 additions & 0 deletions src/log.rs
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/clap-rs/clap-verbosity-flag/issues/54> for more
// information.
pub use log::{Level, LevelFilter};

use crate::VerbosityFilter;

impl From<VerbosityFilter> 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<LevelFilter> 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<VerbosityFilter> for Option<Level> {
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<Option<Level>> for VerbosityFilter {
fn from(level: Option<Level>) -> 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::<OffLevel>::default();
assert_eq!(v.log_level(), None);
assert_eq!(v.log_level_filter(), LevelFilter::Off);

let v = Verbosity::<ErrorLevel>::default();
assert_eq!(v.log_level(), Some(Level::Error));
assert_eq!(v.log_level_filter(), LevelFilter::Error);

let v = Verbosity::<WarnLevel>::default();
assert_eq!(v.log_level(), Some(Level::Warn));
assert_eq!(v.log_level_filter(), LevelFilter::Warn);

let v = Verbosity::<InfoLevel>::default();
assert_eq!(v.log_level(), Some(Level::Info));
assert_eq!(v.log_level_filter(), LevelFilter::Info);

let v = Verbosity::<DebugLevel>::default();
assert_eq!(v.log_level(), Some(Level::Debug));
assert_eq!(v.log_level_filter(), LevelFilter::Debug);

let v = Verbosity::<TraceLevel>::default();
assert_eq!(v.log_level(), Some(Level::Trace));
assert_eq!(v.log_level_filter(), LevelFilter::Trace);
}
}
Loading
Loading