@@ -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
@@ -239,32 +240,27 @@ impl SimpleLogger {
239240 /// .init()
240241 /// .unwrap();
241242 /// ```
243+ //
244+ // This method *must* sort `module_levels` for the [`enabled`](#method.enabled) method to work correctly.
242245 #[ must_use = "You must call init() to begin logging" ]
243246 pub fn with_module_level ( mut self , target : & str , level : LevelFilter ) -> SimpleLogger {
244247 self . module_levels . push ( ( target. to_string ( ) , level) ) ;
245-
246- /* Normally this is only called in `init` to avoid redundancy, but we can't initialize the logger in tests */
247- #[ cfg( test) ]
248248 self . module_levels
249249 . sort_by_key ( |( name, _level) | name. len ( ) . wrapping_neg ( ) ) ;
250-
251250 self
252251 }
253252
254253 /// Override the log level for specific targets.
254+ // This method *must* sort `module_levels` for the [`enabled`](#method.enabled) method to work correctly.
255255 #[ must_use = "You must call init() to begin logging" ]
256256 #[ deprecated(
257257 since = "1.11.0" ,
258258 note = "Use [`with_module_level`](#method.with_module_level) instead. Will be removed in version 2.0.0."
259259 ) ]
260260 pub fn with_target_levels ( mut self , target_levels : HashMap < String , LevelFilter > ) -> SimpleLogger {
261261 self . module_levels = target_levels. into_iter ( ) . collect ( ) ;
262-
263- /* Normally this is only called in `init` to avoid redundancy, but we can't initialize the logger in tests */
264- #[ cfg( test) ]
265262 self . module_levels
266263 . sort_by_key ( |( name, _level) | name. len ( ) . wrapping_neg ( ) ) ;
267-
268264 self
269265 }
270266
@@ -373,12 +369,6 @@ impl SimpleLogger {
373369
374370 /// Configure the logger
375371 pub fn max_level ( & self ) -> LevelFilter {
376- /* Sort all module levels from most specific to least specific. The length of the module
377- * name is used instead of its actual depth to avoid module name parsing.
378- */
379- let mut levels = self . module_levels . clone ( ) ;
380- levels. sort_by_key ( |( name, _level) | name. len ( ) . wrapping_neg ( ) ) ;
381-
382372 let max_level = self . module_levels . iter ( ) . map ( |( _name, level) | level) . copied ( ) . max ( ) ;
383373 max_level
384374 . map ( |lvl| lvl. max ( self . default_level ) )
@@ -640,6 +630,20 @@ mod test {
640630 assert ! ( logger. enabled( & create_log( "chatty_dependency::module" , Level :: Warn ) ) ) ;
641631 }
642632
633+ /// Test that enabled() looks for the most specific target.
634+ #[ test]
635+ fn test_module_levels ( ) {
636+ let logger = SimpleLogger :: new ( )
637+ . with_level ( LevelFilter :: Off )
638+ . with_module_level ( "a" , LevelFilter :: Off )
639+ . with_module_level ( "a::b::c" , LevelFilter :: Off )
640+ . with_module_level ( "a::b" , LevelFilter :: Info ) ;
641+
642+ assert_eq ! ( logger. enabled( & create_log( "a" , Level :: Info ) ) , false ) ;
643+ assert_eq ! ( logger. enabled( & create_log( "a::b" , Level :: Info ) ) , true ) ;
644+ assert_eq ! ( logger. enabled( & create_log( "a::b::c" , Level :: Info ) ) , false ) ;
645+ }
646+
643647 #[ test]
644648 fn test_max_level ( ) {
645649 let builder = SimpleLogger :: new ( ) ;
@@ -694,6 +698,20 @@ mod test {
694698 assert ! ( builder. colors == false ) ;
695699 }
696700
701+ /// > And, without sorting, this would lead to all serde_json logs being treated as if they were configured to
702+ /// > Error level instead of Trace (since to determine the logging level for target, the code finds first match in
703+ /// > module_levels by a string prefix).
704+ #[ test]
705+ fn test_issue_90 ( ) {
706+ let logger = SimpleLogger :: new ( )
707+ . with_level ( LevelFilter :: Off )
708+ . with_module_level ( "serde" , LevelFilter :: Error )
709+ . with_module_level ( "serde_json" , LevelFilter :: Trace ) ;
710+
711+ assert_eq ! ( logger. enabled( & create_log( "serde" , Level :: Trace ) ) , false ) ;
712+ assert_eq ! ( logger. enabled( & create_log( "serde_json" , Level :: Trace ) ) , true ) ;
713+ }
714+
697715 fn create_log ( name : & str , level : Level ) -> Metadata {
698716 let mut builder = Metadata :: builder ( ) ;
699717 builder. level ( level) ;
0 commit comments