Skip to content

Commit 5d8a152

Browse files
authored
refactor: level and level filter (#186)
Signed-off-by: tison <wander4096@gmail.com>
1 parent 4118fd3 commit 5d8a152

File tree

23 files changed

+204
-232
lines changed

23 files changed

+204
-232
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ All notable changes to this project will be documented in this file.
77
### Breaking changes
88

99
* Rename `DefaultTrap` to `BestEffortTrap` for better clarity.
10+
* Add `Level::Critical` variant to represent critical level logs.
11+
* Redesign `LevelFilter`.
1012

1113
## [0.28.1] 2025-10-06
1214

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,16 @@ Configure multiple dispatches with different filters and appenders:
5959

6060
```rust
6161
use logforth::append;
62+
use logforth::record::Level;
6263
use logforth::record::LevelFilter;
6364

6465
fn main() {
6566
logforth::starter_log::builder()
6667
.dispatch(|d| d
67-
.filter(LevelFilter::Error)
68+
.filter(LevelFilter::MoreSevereEqual(Level::Error))
6869
.append(append::Stderr::default()))
6970
.dispatch(|d| d
70-
.filter(LevelFilter::Info)
71+
.filter(LevelFilter::MoreSevereEqual(Level::Info))
7172
.append(append::Stdout::default()))
7273
.apply();
7374

appenders/file/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
//! .unwrap();
3232
//!
3333
//! logforth_core::builder()
34-
//! .dispatch(|d| d.filter(LevelFilter::Trace).append(rolling))
34+
//! .dispatch(|d| d.filter(LevelFilter::All).append(rolling))
3535
//! .apply();
3636
//!
3737
//! log::info!("This log will be written to a rolling file.");

appenders/journald/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ impl Append for Journald {
255255
// write them directly, everything else goes through the put functions
256256
// for property mangling and length-encoding
257257
let priority = match record.level() {
258+
Level::Critical => b"2",
258259
Level::Error => b"3",
259260
Level::Warn => b"4",
260261
Level::Info => b"5",

appenders/opentelemetry/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ impl Drop for OpentelemetryLog {
285285

286286
fn log_level_to_otel_severity(level: Level) -> opentelemetry::logs::Severity {
287287
match level {
288+
Level::Critical => opentelemetry::logs::Severity::Fatal,
288289
Level::Error => opentelemetry::logs::Severity::Error,
289290
Level::Warn => opentelemetry::logs::Severity::Warn,
290291
Level::Info => opentelemetry::logs::Severity::Info,

appenders/syslog/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
//! let append = SyslogBuilder::tcp_well_known().unwrap().build();
2525
//!
2626
//! logforth_core::builder()
27-
//! .dispatch(|d| d.filter(LevelFilter::Trace).append(append))
27+
//! .dispatch(|d| d.filter(LevelFilter::All).append(append))
2828
//! .apply();
2929
//!
3030
//! log::info!("This log will be written to syslog.");
@@ -324,6 +324,7 @@ struct SyslogFormatter {
324324

325325
fn log_level_to_syslog_severity(level: Level) -> fasyslog::Severity {
326326
match level {
327+
Level::Critical => fasyslog::Severity::CRITICAL,
327328
Level::Error => fasyslog::Severity::ERROR,
328329
Level::Warn => fasyslog::Severity::WARNING,
329330
Level::Info => fasyslog::Severity::NOTICE,

core/src/filter/env_filter/mod.rs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ use crate::Diagnostic;
7777
use crate::Error;
7878
use crate::Filter;
7979
use crate::filter::FilterResult;
80+
use crate::record::Level;
8081
use crate::record::LevelFilter;
8182
use crate::record::Metadata;
8283

@@ -96,7 +97,6 @@ pub const DEFAULT_FILTER_ENV: &str = "RUST_LOG";
9697
///
9798
/// Read more from the [module level documentation](self) about the directive syntax and use cases.
9899
///
99-
/// [`Level`]: crate::record::Level
100100
/// [`Record`]: crate::record::Record
101101
#[derive(Debug)]
102102
pub struct EnvFilter {
@@ -121,10 +121,10 @@ impl Filter for EnvFilter {
121121
let name = directive.name.as_deref();
122122
if name.is_none_or(|n| target.starts_with(n)) {
123123
// longest match wins; return immediately
124-
return if directive.level < level {
125-
FilterResult::Reject
126-
} else {
124+
return if directive.level.test(level) {
127125
FilterResult::Neutral
126+
} else {
127+
FilterResult::Reject
128128
};
129129
}
130130
}
@@ -325,7 +325,7 @@ impl EnvFilterBuilder {
325325
if directives.is_empty() {
326326
EnvFilter::from_directives(vec![Directive {
327327
name: None,
328-
level: LevelFilter::Error,
328+
level: LevelFilter::MoreSevereEqual(Level::Error),
329329
}])
330330
} else {
331331
EnvFilter::from_directives(directives)
@@ -422,17 +422,17 @@ fn parse_spec(spec: &str) -> ParseResult {
422422

423423
let (level, name) = match part1 {
424424
None => {
425-
if let Ok(level) = part0.parse() {
425+
if let Some(level) = from_str_for_env(part0) {
426426
// if the single argument is a log level string, treat that as a global fallback
427427
(level, None)
428428
} else {
429-
(LevelFilter::Trace, Some(part0.to_owned()))
429+
(LevelFilter::All, Some(part0.to_owned()))
430430
}
431431
}
432432
Some(part1) => {
433433
if part1.is_empty() {
434-
(LevelFilter::Trace, Some(part0.to_owned()))
435-
} else if let Ok(level) = part1.parse() {
434+
(LevelFilter::All, Some(part0.to_owned()))
435+
} else if let Some(level) = from_str_for_env(part1) {
436436
(level, Some(part0.to_owned()))
437437
} else {
438438
errors.push(format!("malformed logging spec '{part1}'"));
@@ -446,3 +446,15 @@ fn parse_spec(spec: &str) -> ParseResult {
446446

447447
ParseResult { directives, errors }
448448
}
449+
450+
fn from_str_for_env(text: &str) -> Option<LevelFilter> {
451+
if let Ok(level) = Level::from_str(text) {
452+
Some(LevelFilter::MoreSevereEqual(level))
453+
} else if text.eq_ignore_ascii_case("off") {
454+
Some(LevelFilter::Off)
455+
} else if text.eq_ignore_ascii_case("all") {
456+
Some(LevelFilter::All)
457+
} else {
458+
None
459+
}
460+
}

core/src/filter/env_filter/tests.rs

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,13 @@ fn parse_spec_valid() {
4444

4545
assert_eq!(dirs.len(), 3);
4646
assert_eq!(dirs[0].name, Some("crate1::mod1".to_owned()));
47-
assert_eq!(dirs[0].level, LevelFilter::Error);
47+
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Error));
4848

4949
assert_eq!(dirs[1].name, Some("crate1::mod2".to_owned()));
50-
assert_eq!(dirs[1].level, LevelFilter::Trace);
50+
assert_eq!(dirs[1].level, LevelFilter::All);
5151

5252
assert_eq!(dirs[2].name, Some("crate2".to_owned()));
53-
assert_eq!(dirs[2].level, LevelFilter::Debug);
53+
assert_eq!(dirs[2].level, LevelFilter::MoreSevereEqual(Level::Debug));
5454

5555
assert!(errors.is_empty());
5656
}
@@ -65,7 +65,7 @@ fn parse_spec_invalid_crate() {
6565

6666
assert_eq!(dirs.len(), 1);
6767
assert_eq!(dirs[0].name, Some("crate2".to_owned()));
68-
assert_eq!(dirs[0].level, LevelFilter::Debug);
68+
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Debug));
6969

7070
assert_eq!(errors.len(), 1);
7171
assert_snapshot!(
@@ -84,7 +84,7 @@ fn parse_spec_invalid_level() {
8484

8585
assert_eq!(dirs.len(), 1);
8686
assert_eq!(dirs[0].name, Some("crate2".to_owned()));
87-
assert_eq!(dirs[0].level, LevelFilter::Debug);
87+
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Debug));
8888

8989
assert_eq!(errors.len(), 1);
9090
assert_snapshot!(&errors[0], @"malformed logging spec 'noNumber'");
@@ -100,7 +100,7 @@ fn parse_spec_string_level() {
100100

101101
assert_eq!(dirs.len(), 1);
102102
assert_eq!(dirs[0].name, Some("crate2".to_owned()));
103-
assert_eq!(dirs[0].level, LevelFilter::Warn);
103+
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Warn));
104104

105105
assert_eq!(errors.len(), 1);
106106
assert_snapshot!(&errors[0], @"malformed logging spec 'wrong'");
@@ -116,7 +116,7 @@ fn parse_spec_empty_level() {
116116

117117
assert_eq!(dirs.len(), 1);
118118
assert_eq!(dirs[0].name, Some("crate2".to_owned()));
119-
assert_eq!(dirs[0].level, LevelFilter::Trace);
119+
assert_eq!(dirs[0].level, LevelFilter::All);
120120

121121
assert_eq!(errors.len(), 1);
122122
assert_snapshot!(&errors[0], @"malformed logging spec 'wrong'");
@@ -197,9 +197,9 @@ fn parse_spec_global() {
197197
} = parse_spec("warn,crate2=debug");
198198
assert_eq!(dirs.len(), 2);
199199
assert_eq!(dirs[0].name, None);
200-
assert_eq!(dirs[0].level, LevelFilter::Warn);
200+
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Warn));
201201
assert_eq!(dirs[1].name, Some("crate2".to_owned()));
202-
assert_eq!(dirs[1].level, LevelFilter::Debug);
202+
assert_eq!(dirs[1].level, LevelFilter::MoreSevereEqual(Level::Debug));
203203

204204
assert!(errors.is_empty());
205205
}
@@ -213,7 +213,7 @@ fn parse_spec_global_bare_warn_lc() {
213213
} = parse_spec("warn");
214214
assert_eq!(dirs.len(), 1);
215215
assert_eq!(dirs[0].name, None);
216-
assert_eq!(dirs[0].level, LevelFilter::Warn);
216+
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Warn));
217217

218218
assert!(errors.is_empty());
219219
}
@@ -227,7 +227,7 @@ fn parse_spec_global_bare_warn_uc() {
227227
} = parse_spec("WARN");
228228
assert_eq!(dirs.len(), 1);
229229
assert_eq!(dirs[0].name, None);
230-
assert_eq!(dirs[0].level, LevelFilter::Warn);
230+
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Warn));
231231

232232
assert!(errors.is_empty());
233233
}
@@ -241,7 +241,7 @@ fn parse_spec_global_bare_warn_mixed() {
241241
} = parse_spec("wArN");
242242
assert_eq!(dirs.len(), 1);
243243
assert_eq!(dirs[0].name, None);
244-
assert_eq!(dirs[0].level, LevelFilter::Warn);
244+
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Warn));
245245

246246
assert!(errors.is_empty());
247247
}
@@ -256,7 +256,7 @@ fn parse_spec_multiple_invalid_crates() {
256256

257257
assert_eq!(dirs.len(), 1);
258258
assert_eq!(dirs[0].name, Some("crate2".to_owned()));
259-
assert_eq!(dirs[0].level, LevelFilter::Debug);
259+
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Debug));
260260

261261
assert_eq!(errors.len(), 2);
262262
assert_snapshot!(
@@ -279,7 +279,7 @@ fn parse_spec_multiple_invalid_levels() {
279279

280280
assert_eq!(dirs.len(), 1);
281281
assert_eq!(dirs[0].name, Some("crate2".to_owned()));
282-
assert_eq!(dirs[0].level, LevelFilter::Debug);
282+
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Debug));
283283

284284
assert_eq!(errors.len(), 2);
285285
assert_snapshot!(&errors[0], @"malformed logging spec 'noNumber'");
@@ -296,7 +296,7 @@ fn parse_spec_invalid_crate_and_level() {
296296

297297
assert_eq!(dirs.len(), 1);
298298
assert_eq!(dirs[0].name, Some("crate2".to_owned()));
299-
assert_eq!(dirs[0].level, LevelFilter::Debug);
299+
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Debug));
300300

301301
assert_eq!(errors.len(), 2);
302302
assert_snapshot!(
@@ -328,7 +328,7 @@ fn parse_error_message_multiple_errors() {
328328
#[test]
329329
fn filter_info() {
330330
let logger = EnvFilterBuilder::default()
331-
.filter_level(LevelFilter::Info)
331+
.filter_level(LevelFilter::MoreSevereEqual(Level::Info))
332332
.build();
333333
assert!(!logger.rejected(Level::Info, "crate1"));
334334
assert!(logger.rejected(Level::Debug, "crate1"));
@@ -337,9 +337,9 @@ fn filter_info() {
337337
#[test]
338338
fn filter_beginning_longest_match() {
339339
let logger = EnvFilterBuilder::default()
340-
.filter_module("crate2", LevelFilter::Info)
341-
.filter_module("crate2::mod", LevelFilter::Debug)
342-
.filter_module("crate1::mod1", LevelFilter::Warn)
340+
.filter_module("crate2", LevelFilter::MoreSevereEqual(Level::Info))
341+
.filter_module("crate2::mod", LevelFilter::MoreSevereEqual(Level::Debug))
342+
.filter_module("crate1::mod1", LevelFilter::MoreSevereEqual(Level::Warn))
343343
.build();
344344
assert!(!logger.rejected(Level::Debug, "crate2::mod1"));
345345
assert!(logger.rejected(Level::Debug, "crate2"));
@@ -361,7 +361,12 @@ fn filter_beginning_longest_match() {
361361
fn ensure_tests_cover_level_universe() {
362362
let level_universe: Level = Level::Trace; // use of trace variant is arbitrary
363363
match level_universe {
364-
Level::Error | Level::Warn | Level::Info | Level::Debug | Level::Trace => (),
364+
Level::Critical
365+
| Level::Error
366+
| Level::Warn
367+
| Level::Info
368+
| Level::Debug
369+
| Level::Trace => (),
365370
}
366371
}
367372

@@ -552,11 +557,11 @@ fn match_full_path() {
552557
let logger = EnvFilter::from_directives(vec![
553558
Directive {
554559
name: Some("crate2".to_owned()),
555-
level: LevelFilter::Info,
560+
level: LevelFilter::MoreSevereEqual(Level::Info),
556561
},
557562
Directive {
558563
name: Some("crate1::mod1".to_owned()),
559-
level: LevelFilter::Warn,
564+
level: LevelFilter::MoreSevereEqual(Level::Warn),
560565
},
561566
]);
562567
assert!(!logger.rejected(Level::Warn, "crate1::mod1"));
@@ -570,11 +575,11 @@ fn no_match() {
570575
let logger = EnvFilter::from_directives(vec![
571576
Directive {
572577
name: Some("crate2".to_owned()),
573-
level: LevelFilter::Info,
578+
level: LevelFilter::MoreSevereEqual(Level::Info),
574579
},
575580
Directive {
576581
name: Some("crate1::mod1".to_owned()),
577-
level: LevelFilter::Warn,
582+
level: LevelFilter::MoreSevereEqual(Level::Warn),
578583
},
579584
]);
580585
assert!(logger.rejected(Level::Warn, "crate3"));
@@ -585,11 +590,11 @@ fn match_beginning() {
585590
let logger = EnvFilter::from_directives(vec![
586591
Directive {
587592
name: Some("crate2".to_owned()),
588-
level: LevelFilter::Info,
593+
level: LevelFilter::MoreSevereEqual(Level::Info),
589594
},
590595
Directive {
591596
name: Some("crate1::mod1".to_owned()),
592-
level: LevelFilter::Warn,
597+
level: LevelFilter::MoreSevereEqual(Level::Warn),
593598
},
594599
]);
595600
assert!(!logger.rejected(Level::Info, "crate2::mod1"));
@@ -600,15 +605,15 @@ fn match_beginning_longest_match() {
600605
let logger = EnvFilter::from_directives(vec![
601606
Directive {
602607
name: Some("crate2".to_owned()),
603-
level: LevelFilter::Info,
608+
level: LevelFilter::MoreSevereEqual(Level::Info),
604609
},
605610
Directive {
606611
name: Some("crate2::mod".to_owned()),
607-
level: LevelFilter::Debug,
612+
level: LevelFilter::MoreSevereEqual(Level::Debug),
608613
},
609614
Directive {
610615
name: Some("crate1::mod1".to_owned()),
611-
level: LevelFilter::Warn,
616+
level: LevelFilter::MoreSevereEqual(Level::Warn),
612617
},
613618
]);
614619
assert!(!logger.rejected(Level::Debug, "crate2::mod1"));
@@ -620,11 +625,11 @@ fn match_default() {
620625
let logger = EnvFilter::from_directives(vec![
621626
Directive {
622627
name: None,
623-
level: LevelFilter::Info,
628+
level: LevelFilter::MoreSevereEqual(Level::Info),
624629
},
625630
Directive {
626631
name: Some("crate1::mod1".to_owned()),
627-
level: LevelFilter::Warn,
632+
level: LevelFilter::MoreSevereEqual(Level::Warn),
628633
},
629634
]);
630635
assert!(!logger.rejected(Level::Warn, "crate1::mod1"));
@@ -636,7 +641,7 @@ fn zero_level() {
636641
let logger = EnvFilter::from_directives(vec![
637642
Directive {
638643
name: None,
639-
level: LevelFilter::Info,
644+
level: LevelFilter::MoreSevereEqual(Level::Info),
640645
},
641646
Directive {
642647
name: Some("crate1::mod1".to_owned()),

core/src/filter/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub trait Filter: fmt::Debug + Send + Sync + 'static {
4949

5050
impl Filter for LevelFilter {
5151
fn enabled(&self, metadata: &Metadata, _: &[Box<dyn Diagnostic>]) -> FilterResult {
52-
if metadata.level() <= *self {
52+
if self.test(metadata.level()) {
5353
FilterResult::Neutral
5454
} else {
5555
FilterResult::Reject

0 commit comments

Comments
 (0)