@@ -9,7 +9,6 @@ namespace Foundatio.Xunit;
99
1010public class TestLogger : ILoggerFactory
1111{
12- private readonly Dictionary < string , LogLevel > _logLevels = new ( ) ;
1312 private readonly Queue < LogEntry > _logEntries = new ( ) ;
1413 private int _logEntriesWritten ;
1514
@@ -19,7 +18,7 @@ public TestLogger(Action<TestLoggerOptions> configure = null)
1918 configure ? . Invoke ( Options ) ;
2019
2120 foreach ( var logLevel in Options . LogLevels )
22- _logLevels [ logLevel . Key ] = logLevel . Value ;
21+ SetLogLevel ( logLevel . Key , logLevel . Value ) ;
2322 }
2423
2524 public TestLogger ( ITestOutputHelper output , Action < TestLoggerOptions > configure = null )
@@ -35,21 +34,21 @@ public TestLogger(ITestOutputHelper output, Action<TestLoggerOptions> configure
3534 configure ? . Invoke ( Options ) ;
3635
3736 foreach ( var logLevel in Options . LogLevels )
38- _logLevels [ logLevel . Key ] = logLevel . Value ;
37+ SetLogLevel ( logLevel . Key , logLevel . Value ) ;
3938 }
4039
4140 public TestLogger ( TestLoggerOptions options )
4241 {
4342 Options = options ?? new TestLoggerOptions ( ) ;
4443
4544 foreach ( var logLevel in Options . LogLevels )
46- _logLevels [ logLevel . Key ] = logLevel . Value ;
45+ SetLogLevel ( logLevel . Key , logLevel . Value ) ;
4746
4847 }
4948
5049 public TestLoggerOptions Options { get ; }
5150
52- public LogLevel DefaultMinimumLevel
51+ public LogLevel DefaultLogLevel
5352 {
5453 get => Options . DefaultLogLevel ;
5554 set => Options . DefaultLogLevel = value ;
@@ -74,10 +73,20 @@ public void Reset()
7473 lock ( _logEntries )
7574 {
7675 _logEntries . Clear ( ) ;
77- _logLevels . Clear ( ) ;
76+
77+ _lock . EnterWriteLock ( ) ;
78+ try
79+ {
80+ _root . Children . Clear ( ) ;
81+ _root . Level = null ;
82+ }
83+ finally
84+ {
85+ _lock . ExitWriteLock ( ) ;
86+ }
7887
7988 foreach ( var logLevel in Options . LogLevels )
80- _logLevels [ logLevel . Key ] = logLevel . Value ;
89+ SetLogLevel ( logLevel . Key , logLevel . Value ) ;
8190
8291 Interlocked . Exchange ( ref _logEntriesWritten , 0 ) ;
8392 }
@@ -116,15 +125,74 @@ public void AddProvider(ILoggerProvider loggerProvider) { }
116125
117126 public bool IsEnabled ( string category , LogLevel logLevel )
118127 {
119- if ( _logLevels . TryGetValue ( category , out var categoryLevel ) )
120- return logLevel >= categoryLevel ;
128+ ReadOnlySpan < char > span = category . AsSpan ( ) ;
129+ Span < ( int Start , int Length ) > segments = stackalloc ( int , int ) [ 20 ] ;
130+
131+ int count = 0 ;
132+ int start = 0 ;
133+
134+ for ( int i = 0 ; i <= span . Length ; i ++ )
135+ {
136+ if ( i != span . Length && span [ i ] != '.' )
137+ continue ;
138+
139+ segments [ count ++ ] = ( start , i - start ) ;
140+ start = i + 1 ;
141+
142+ if ( count == segments . Length )
143+ break ;
144+ }
145+
146+ _lock . EnterReadLock ( ) ;
147+ try {
148+ var current = _root ;
149+ LogLevel effectiveLevel = DefaultLogLevel ;
150+
151+ for ( int i = 0 ; i < count ; i ++ ) {
152+ var segment = span . Slice ( segments [ i ] . Start , segments [ i ] . Length ) ;
153+ bool found = false ;
121154
122- return logLevel >= Options . DefaultLogLevel ;
155+ foreach ( var kvp in current . Children )
156+ {
157+ if ( ! segment . Equals ( kvp . Key . AsSpan ( ) , StringComparison . Ordinal ) )
158+ continue ;
159+
160+ current = kvp . Value ;
161+ found = true ;
162+
163+ if ( current . Level . HasValue )
164+ effectiveLevel = current . Level . Value ;
165+
166+ break ;
167+ }
168+
169+ if ( ! found )
170+ break ;
171+ }
172+
173+ return logLevel >= effectiveLevel ;
174+ } finally {
175+ _lock . ExitReadLock ( ) ;
176+ }
123177 }
124178
125179 public void SetLogLevel ( string category , LogLevel minLogLevel )
126180 {
127- _logLevels [ category ] = minLogLevel ;
181+ string [ ] parts = category . Split ( '.' ) ;
182+ _lock . EnterWriteLock ( ) ;
183+ try {
184+ var current = _root ;
185+ foreach ( string part in parts ) {
186+ if ( ! current . Children . TryGetValue ( part , out var child ) ) {
187+ child = new Node ( ) ;
188+ current . Children [ part ] = child ;
189+ }
190+ current = child ;
191+ }
192+ current . Level = minLogLevel ;
193+ } finally {
194+ _lock . ExitWriteLock ( ) ;
195+ }
128196 }
129197
130198 public void SetLogLevel < T > ( LogLevel minLogLevel )
@@ -133,4 +201,12 @@ public void SetLogLevel<T>(LogLevel minLogLevel)
133201 }
134202
135203 public void Dispose ( ) { }
204+
205+ private class Node {
206+ public readonly Dictionary < string , Node > Children = new ( StringComparer . OrdinalIgnoreCase ) ;
207+ public LogLevel ? Level ;
208+ }
209+
210+ private readonly Node _root = new ( ) ;
211+ private readonly ReaderWriterLockSlim _lock = new ( ) ;
136212}
0 commit comments