Skip to content

Commit cdd2901

Browse files
authored
Merge pull request #123 from joshka/jm/additional-log-levels
feat: add additional log levels
2 parents a7e305e + 2192330 commit cdd2901

File tree

2 files changed

+196
-4
lines changed

2 files changed

+196
-4
lines changed

examples/log_level.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//! Demonstrates how to set the default log level for the logger to something other than the default
2+
//! (`ErrorLevel`). This is done with multiple subcommands, each with their own verbosity level.
3+
4+
use clap::{Parser, Subcommand};
5+
use clap_verbosity_flag::{
6+
DebugLevel, ErrorLevel, InfoLevel, OffLevel, TraceLevel, Verbosity, WarnLevel,
7+
};
8+
9+
#[derive(Debug, Parser)]
10+
struct Cli {
11+
#[command(subcommand)]
12+
command: Command,
13+
}
14+
15+
#[derive(Debug, Subcommand)]
16+
enum Command {
17+
Off {
18+
#[command(flatten)]
19+
verbose: Verbosity<OffLevel>,
20+
},
21+
Error {
22+
#[command(flatten)]
23+
verbose: Verbosity<ErrorLevel>,
24+
},
25+
Warn {
26+
#[command(flatten)]
27+
verbose: Verbosity<WarnLevel>,
28+
},
29+
Info {
30+
#[command(flatten)]
31+
verbose: Verbosity<InfoLevel>,
32+
},
33+
Debug {
34+
#[command(flatten)]
35+
verbose: Verbosity<DebugLevel>,
36+
},
37+
Trace {
38+
#[command(flatten)]
39+
verbose: Verbosity<TraceLevel>,
40+
},
41+
}
42+
43+
impl Command {
44+
fn log_level_filter(&self) -> log::LevelFilter {
45+
match self {
46+
Command::Off { verbose } => verbose.log_level_filter(),
47+
Command::Error { verbose } => verbose.log_level_filter(),
48+
Command::Warn { verbose } => verbose.log_level_filter(),
49+
Command::Info { verbose } => verbose.log_level_filter(),
50+
Command::Debug { verbose } => verbose.log_level_filter(),
51+
Command::Trace { verbose } => verbose.log_level_filter(),
52+
}
53+
}
54+
}
55+
56+
fn main() {
57+
let cli = Cli::parse();
58+
env_logger::Builder::new()
59+
.filter_level(cli.command.log_level_filter())
60+
.init();
61+
62+
log::error!("Engines exploded");
63+
log::warn!("Engines smoking");
64+
log::info!("Engines exist");
65+
log::debug!("Engine temperature is 200 degrees");
66+
log::trace!("Engine subsection is 300 degrees");
67+
}

src/lib.rs

Lines changed: 129 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@
4040
//! - `-vvv` show debug
4141
//! - `-vvvv` show trace
4242
//!
43-
//! You can also customize the default logging level:
43+
//! By default, the log level is set to Error. To customize this to a different level, pass a type
44+
//! implementing the [`LogLevel`] trait to [`Verbosity`]:
45+
//!
4446
//! ```rust,no_run
4547
//! # use clap::Parser;
4648
//! use clap_verbosity_flag::{Verbosity, InfoLevel};
@@ -190,7 +192,6 @@ pub trait LogLevel {
190192
}
191193

192194
/// Default to [`log::Level::Error`]
193-
#[allow(clippy::exhaustive_structs)]
194195
#[derive(Copy, Clone, Debug, Default)]
195196
pub struct ErrorLevel;
196197

@@ -201,7 +202,6 @@ impl LogLevel for ErrorLevel {
201202
}
202203

203204
/// Default to [`log::Level::Warn`]
204-
#[allow(clippy::exhaustive_structs)]
205205
#[derive(Copy, Clone, Debug, Default)]
206206
pub struct WarnLevel;
207207

@@ -212,7 +212,6 @@ impl LogLevel for WarnLevel {
212212
}
213213

214214
/// Default to [`log::Level::Info`]
215-
#[allow(clippy::exhaustive_structs)]
216215
#[derive(Copy, Clone, Debug, Default)]
217216
pub struct InfoLevel;
218217

@@ -222,6 +221,36 @@ impl LogLevel for InfoLevel {
222221
}
223222
}
224223

224+
/// Default to [`log::Level::Debug`]
225+
#[derive(Copy, Clone, Debug, Default)]
226+
pub struct DebugLevel;
227+
228+
impl LogLevel for DebugLevel {
229+
fn default() -> Option<Level> {
230+
Some(Level::Debug)
231+
}
232+
}
233+
234+
/// Default to [`log::Level::Trace`]
235+
#[derive(Copy, Clone, Debug, Default)]
236+
pub struct TraceLevel;
237+
238+
impl LogLevel for TraceLevel {
239+
fn default() -> Option<Level> {
240+
Some(Level::Trace)
241+
}
242+
}
243+
244+
/// Default to no logging (i.e. `None` or [`log::LevelFilter::Off`])
245+
#[derive(Copy, Clone, Debug, Default)]
246+
pub struct OffLevel;
247+
248+
impl LogLevel for OffLevel {
249+
fn default() -> Option<Level> {
250+
None
251+
}
252+
}
253+
225254
#[cfg(test)]
226255
mod test {
227256
use super::*;
@@ -238,6 +267,38 @@ mod test {
238267
Cli::command().debug_assert();
239268
}
240269

270+
#[test]
271+
fn verbosity_off_level() {
272+
let tests = [
273+
// verbose, quiet, expected_level, expected_filter
274+
(0, 0, None, LevelFilter::Off),
275+
(1, 0, Some(Level::Error), LevelFilter::Error),
276+
(2, 0, Some(Level::Warn), LevelFilter::Warn),
277+
(3, 0, Some(Level::Info), LevelFilter::Info),
278+
(4, 0, Some(Level::Debug), LevelFilter::Debug),
279+
(5, 0, Some(Level::Trace), LevelFilter::Trace),
280+
(6, 0, Some(Level::Trace), LevelFilter::Trace),
281+
(255, 0, Some(Level::Trace), LevelFilter::Trace),
282+
(0, 1, None, LevelFilter::Off),
283+
(0, 255, None, LevelFilter::Off),
284+
(255, 255, None, LevelFilter::Off),
285+
];
286+
287+
for (verbose, quiet, expected_level, expected_filter) in tests.iter() {
288+
let v = Verbosity::<OffLevel>::new(*verbose, *quiet);
289+
assert_eq!(
290+
v.log_level(),
291+
*expected_level,
292+
"verbose = {verbose}, quiet = {quiet}"
293+
);
294+
assert_eq!(
295+
v.log_level_filter(),
296+
*expected_filter,
297+
"verbose = {verbose}, quiet = {quiet}"
298+
);
299+
}
300+
}
301+
241302
#[test]
242303
fn verbosity_error_level() {
243304
let tests = [
@@ -333,4 +394,68 @@ mod test {
333394
);
334395
}
335396
}
397+
398+
#[test]
399+
fn verbosity_debug_level() {
400+
let tests = [
401+
// verbose, quiet, expected_level, expected_filter
402+
(0, 0, Some(Level::Debug), LevelFilter::Debug),
403+
(1, 0, Some(Level::Trace), LevelFilter::Trace),
404+
(2, 0, Some(Level::Trace), LevelFilter::Trace),
405+
(255, 0, Some(Level::Trace), LevelFilter::Trace),
406+
(0, 1, Some(Level::Info), LevelFilter::Info),
407+
(0, 2, Some(Level::Warn), LevelFilter::Warn),
408+
(0, 3, Some(Level::Error), LevelFilter::Error),
409+
(0, 4, None, LevelFilter::Off),
410+
(0, 5, None, LevelFilter::Off),
411+
(0, 255, None, LevelFilter::Off),
412+
(255, 255, Some(Level::Debug), LevelFilter::Debug),
413+
];
414+
415+
for (verbose, quiet, expected_level, expected_filter) in tests.iter() {
416+
let v = Verbosity::<DebugLevel>::new(*verbose, *quiet);
417+
assert_eq!(
418+
v.log_level(),
419+
*expected_level,
420+
"verbose = {verbose}, quiet = {quiet}"
421+
);
422+
assert_eq!(
423+
v.log_level_filter(),
424+
*expected_filter,
425+
"verbose = {verbose}, quiet = {quiet}"
426+
);
427+
}
428+
}
429+
430+
#[test]
431+
fn verbosity_trace_level() {
432+
let tests = [
433+
// verbose, quiet, expected_level, expected_filter
434+
(0, 0, Some(Level::Trace), LevelFilter::Trace),
435+
(1, 0, Some(Level::Trace), LevelFilter::Trace),
436+
(255, 0, Some(Level::Trace), LevelFilter::Trace),
437+
(0, 1, Some(Level::Debug), LevelFilter::Debug),
438+
(0, 2, Some(Level::Info), LevelFilter::Info),
439+
(0, 3, Some(Level::Warn), LevelFilter::Warn),
440+
(0, 4, Some(Level::Error), LevelFilter::Error),
441+
(0, 5, None, LevelFilter::Off),
442+
(0, 6, None, LevelFilter::Off),
443+
(0, 255, None, LevelFilter::Off),
444+
(255, 255, Some(Level::Trace), LevelFilter::Trace),
445+
];
446+
447+
for (verbose, quiet, expected_level, expected_filter) in tests.iter() {
448+
let v = Verbosity::<TraceLevel>::new(*verbose, *quiet);
449+
assert_eq!(
450+
v.log_level(),
451+
*expected_level,
452+
"verbose = {verbose}, quiet = {quiet}"
453+
);
454+
assert_eq!(
455+
v.log_level_filter(),
456+
*expected_filter,
457+
"verbose = {verbose}, quiet = {quiet}"
458+
);
459+
}
460+
}
336461
}

0 commit comments

Comments
 (0)