@@ -66,8 +66,9 @@ pub struct SimpleLogger {
66
66
/// The specific logging level for each module
67
67
///
68
68
/// 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.
71
72
module_levels : Vec < ( String , LevelFilter ) > ,
72
73
73
74
/// Whether to include thread names (and IDs) or not
@@ -209,32 +210,25 @@ impl SimpleLogger {
209
210
/// .init()
210
211
/// .unwrap();
211
212
/// ```
213
+ //
214
+ // This method *must* sort `module_levels` for the [`enabled`](#method.enabled) method to work correctly.
212
215
#[ must_use = "You must call init() to begin logging" ]
213
216
pub fn with_module_level ( mut self , target : & str , level : LevelFilter ) -> SimpleLogger {
214
217
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 ( ) ) ;
221
219
self
222
220
}
223
221
224
222
/// Override the log level for specific targets.
223
+ // This method *must* sort `module_levels` for the [`enabled`](#method.enabled) method to work correctly.
225
224
#[ must_use = "You must call init() to begin logging" ]
226
225
#[ deprecated(
227
226
since = "1.11.0" ,
228
227
note = "Use [`with_module_level`](#method.with_module_level) instead. Will be removed in version 2.0.0."
229
228
) ]
230
229
pub fn with_target_levels ( mut self , target_levels : HashMap < String , LevelFilter > ) -> SimpleLogger {
231
230
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 ( ) ) ;
238
232
self
239
233
}
240
234
@@ -343,12 +337,6 @@ impl SimpleLogger {
343
337
344
338
/// Configure the logger
345
339
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
-
352
340
let max_level = self . module_levels . iter ( ) . map ( |( _name, level) | level) . copied ( ) . max ( ) ;
353
341
max_level
354
342
. map ( |lvl| lvl. max ( self . default_level ) )
@@ -610,6 +598,20 @@ mod test {
610
598
assert ! ( logger. enabled( & create_log( "chatty_dependency::module" , Level :: Warn ) ) ) ;
611
599
}
612
600
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
+
613
615
#[ test]
614
616
fn test_max_level ( ) {
615
617
let builder = SimpleLogger :: new ( ) ;
@@ -664,6 +666,20 @@ mod test {
664
666
assert ! ( builder. colors == false ) ;
665
667
}
666
668
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
+
667
683
fn create_log ( name : & str , level : Level ) -> Metadata {
668
684
let mut builder = Metadata :: builder ( ) ;
669
685
builder. level ( level) ;
0 commit comments