Skip to content

Commit 5f1cfd9

Browse files
committed
Ensure module_levels is always sorted
Fixes #90
1 parent 50729f3 commit 5f1cfd9

File tree

2 files changed

+37
-21
lines changed

2 files changed

+37
-21
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "simple_logger"
3-
version = "4.3.1"
3+
version = "4.3.2"
44
license = "MIT"
55
authors = ["Sam Clements <[email protected]>"]
66
description = "A logger that prints all messages with a readable output format"

src/lib.rs

+36-20
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,9 @@ pub struct SimpleLogger {
6666
/// The specific logging level for each module
6767
///
6868
/// This is used to override the default value for some specific modules.
69-
/// After initialization, the vector is sorted so that the first (prefix) match
70-
/// directly gives us the desired log level.
69+
///
70+
/// This must be sorted from most-specific to least-specific, so that [`enabled`](#method.enabled) can scan the
71+
/// vector for the first match to give us the desired log level for a module.
7172
module_levels: Vec<(String, LevelFilter)>,
7273

7374
/// Whether to include thread names (and IDs) or not
@@ -209,32 +210,25 @@ impl SimpleLogger {
209210
/// .init()
210211
/// .unwrap();
211212
/// ```
213+
//
214+
// This method *must* sort `module_levels` for the [`enabled`](#method.enabled) method to work correctly.
212215
#[must_use = "You must call init() to begin logging"]
213216
pub fn with_module_level(mut self, target: &str, level: LevelFilter) -> SimpleLogger {
214217
self.module_levels.push((target.to_string(), level));
215-
216-
/* Normally this is only called in `init` to avoid redundancy, but we can't initialize the logger in tests */
217-
#[cfg(test)]
218-
self.module_levels
219-
.sort_by_key(|(name, _level)| name.len().wrapping_neg());
220-
218+
self.module_levels.sort_by_key(|(name, _level)| name.len().wrapping_neg());
221219
self
222220
}
223221

224222
/// Override the log level for specific targets.
223+
// This method *must* sort `module_levels` for the [`enabled`](#method.enabled) method to work correctly.
225224
#[must_use = "You must call init() to begin logging"]
226225
#[deprecated(
227226
since = "1.11.0",
228227
note = "Use [`with_module_level`](#method.with_module_level) instead. Will be removed in version 2.0.0."
229228
)]
230229
pub fn with_target_levels(mut self, target_levels: HashMap<String, LevelFilter>) -> SimpleLogger {
231230
self.module_levels = target_levels.into_iter().collect();
232-
233-
/* Normally this is only called in `init` to avoid redundancy, but we can't initialize the logger in tests */
234-
#[cfg(test)]
235-
self.module_levels
236-
.sort_by_key(|(name, _level)| name.len().wrapping_neg());
237-
231+
self.module_levels.sort_by_key(|(name, _level)| name.len().wrapping_neg());
238232
self
239233
}
240234

@@ -343,12 +337,6 @@ impl SimpleLogger {
343337

344338
/// Configure the logger
345339
pub fn max_level(&self) -> LevelFilter {
346-
/* Sort all module levels from most specific to least specific. The length of the module
347-
* name is used instead of its actual depth to avoid module name parsing.
348-
*/
349-
let mut levels = self.module_levels.clone();
350-
levels.sort_by_key(|(name, _level)| name.len().wrapping_neg());
351-
352340
let max_level = self.module_levels.iter().map(|(_name, level)| level).copied().max();
353341
max_level
354342
.map(|lvl| lvl.max(self.default_level))
@@ -610,6 +598,20 @@ mod test {
610598
assert!(logger.enabled(&create_log("chatty_dependency::module", Level::Warn)));
611599
}
612600

601+
/// Test that enabled() looks for the most specific target.
602+
#[test]
603+
fn test_module_levels() {
604+
let logger = SimpleLogger::new()
605+
.with_level(LevelFilter::Off)
606+
.with_module_level("a", LevelFilter::Off)
607+
.with_module_level("a::b::c", LevelFilter::Off)
608+
.with_module_level("a::b", LevelFilter::Info);
609+
610+
assert_eq!(logger.enabled(&create_log("a", Level::Info)), false);
611+
assert_eq!(logger.enabled(&create_log("a::b", Level::Info)), true);
612+
assert_eq!(logger.enabled(&create_log("a::b::c", Level::Info)), false);
613+
}
614+
613615
#[test]
614616
fn test_max_level() {
615617
let builder = SimpleLogger::new();
@@ -664,6 +666,20 @@ mod test {
664666
assert!(builder.colors == false);
665667
}
666668

669+
/// > And, without sorting, this would lead to all serde_json logs being treated as if they were configured to
670+
/// > Error level instead of Trace (since to determine the logging level for target, the code finds first match in
671+
/// > module_levels by a string prefix).
672+
#[test]
673+
fn test_issue_90() {
674+
let logger = SimpleLogger::new()
675+
.with_level(LevelFilter::Off)
676+
.with_module_level("serde", LevelFilter::Error)
677+
.with_module_level("serde_json", LevelFilter::Trace);
678+
679+
assert_eq!(logger.enabled(&create_log("serde", Level::Trace)), false);
680+
assert_eq!(logger.enabled(&create_log("serde_json", Level::Trace)), true);
681+
}
682+
667683
fn create_log(name: &str, level: Level) -> Metadata {
668684
let mut builder = Metadata::builder();
669685
builder.level(level);

0 commit comments

Comments
 (0)