12
12
using Microsoft . AspNetCore . Testing ;
13
13
using Microsoft . Extensions . Hosting . Internal ;
14
14
using Microsoft . Extensions . Logging . Abstractions ;
15
+ using Microsoft . Extensions . Logging . Testing ;
15
16
using Microsoft . Net . Http . Headers ;
16
17
using Xunit ;
17
18
@@ -23,6 +24,8 @@ public class FileLoggerProcessorTests
23
24
private string _messageOne = "Message one" ;
24
25
private string _messageTwo = "Message two" ;
25
26
private string _messageThree = "Message three" ;
27
+ private string _messageFour = "Message four" ;
28
+ private readonly DateTime _today = DateTime . UtcNow ;
26
29
27
30
public FileLoggerProcessorTests ( )
28
31
{
@@ -190,6 +193,60 @@ public async Task RespectsMaxFileCount()
190
193
}
191
194
}
192
195
196
+ [ Fact ]
197
+ public async Task StopsLoggingAfter10000Files ( )
198
+ {
199
+ var path = Path . Combine ( TempPath , Path . GetRandomFileName ( ) ) ;
200
+ Directory . CreateDirectory ( path ) ;
201
+
202
+ try
203
+ {
204
+ string lastFileName ;
205
+ var options = new W3CLoggerOptions ( )
206
+ {
207
+ LogDirectory = path ,
208
+ FileSizeLimit = 5 ,
209
+ RetainedFileCountLimit = 10000
210
+ } ;
211
+ var testSink = new TestSink ( ) ;
212
+ var testLogger = new TestLoggerFactory ( testSink , enabled : true ) ;
213
+ await using ( var logger = new FileLoggerProcessor ( new OptionsWrapperMonitor < W3CLoggerOptions > ( options ) , new HostingEnvironment ( ) , testLogger ) )
214
+ {
215
+ for ( int i = 0 ; i < 10000 ; i ++ )
216
+ {
217
+ logger . EnqueueMessage ( _messageOne ) ;
218
+ }
219
+ lastFileName = Path . Combine ( path , FormattableString . Invariant ( $ "{ options . FileName } { _today . Year : 0000} { _today . Month : 00} { _today . Day : 00} .9999.txt") ) ;
220
+ await WaitForFile ( lastFileName , _messageOne . Length ) . DefaultTimeout ( ) ;
221
+
222
+ // directory is full, no warnings yet
223
+ Assert . Equal ( 0 , testSink . Writes . Count ) ;
224
+
225
+ logger . EnqueueMessage ( _messageOne ) ;
226
+ await WaitForCondition ( ( ) => testSink . Writes . FirstOrDefault ( ) ? . EventId . Name == "MaxFilesReached" ) . DefaultTimeout ( ) ;
227
+ }
228
+
229
+ Assert . Equal ( 10000 , new DirectoryInfo ( path )
230
+ . GetFiles ( )
231
+ . ToArray ( ) . Length ) ;
232
+
233
+ // restarting the logger should do nothing since the folder is still full
234
+ var testSink2 = new TestSink ( ) ;
235
+ var testLogger2 = new TestLoggerFactory ( testSink2 , enabled : true ) ;
236
+ await using ( var logger = new FileLoggerProcessor ( new OptionsWrapperMonitor < W3CLoggerOptions > ( options ) , new HostingEnvironment ( ) , testLogger2 ) )
237
+ {
238
+ Assert . Equal ( 0 , testSink2 . Writes . Count ) ;
239
+
240
+ logger . EnqueueMessage ( _messageOne ) ;
241
+ await WaitForCondition ( ( ) => testSink2 . Writes . FirstOrDefault ( ) ? . EventId . Name == "MaxFilesReached" ) . DefaultTimeout ( ) ;
242
+ }
243
+ }
244
+ finally
245
+ {
246
+ Helpers . DisposeDirectory ( path ) ;
247
+ }
248
+ }
249
+
193
250
[ Fact ]
194
251
public async Task InstancesWriteToSameDirectory ( )
195
252
{
@@ -340,6 +397,66 @@ public async Task WritesToNewFileOnNewInstance()
340
397
}
341
398
}
342
399
400
+ [ Fact ]
401
+ public async Task RollsTextFilesWhenFirstLogOfDayIsMissing ( )
402
+ {
403
+ var path = Path . Combine ( TempPath , Path . GetRandomFileName ( ) ) ;
404
+ Directory . CreateDirectory ( path ) ;
405
+
406
+ try
407
+ {
408
+ var options = new W3CLoggerOptions ( )
409
+ {
410
+ LogDirectory = path ,
411
+ FileSizeLimit = 5 ,
412
+ RetainedFileCountLimit = 2 ,
413
+ } ;
414
+ var fileName1 = Path . Combine ( path , FormattableString . Invariant ( $ "{ options . FileName } { _today . Year : 0000} { _today . Month : 00} { _today . Day : 00} .0000.txt") ) ;
415
+ var fileName2 = Path . Combine ( path , FormattableString . Invariant ( $ "{ options . FileName } { _today . Year : 0000} { _today . Month : 00} { _today . Day : 00} .0001.txt") ) ;
416
+ var fileName3 = Path . Combine ( path , FormattableString . Invariant ( $ "{ options . FileName } { _today . Year : 0000} { _today . Month : 00} { _today . Day : 00} .0002.txt") ) ;
417
+ var fileName4 = Path . Combine ( path , FormattableString . Invariant ( $ "{ options . FileName } { _today . Year : 0000} { _today . Month : 00} { _today . Day : 00} .0003.txt") ) ;
418
+
419
+ await using ( var logger = new FileLoggerProcessor ( new OptionsWrapperMonitor < W3CLoggerOptions > ( options ) , new HostingEnvironment ( ) , NullLoggerFactory . Instance ) )
420
+ {
421
+ logger . EnqueueMessage ( _messageOne ) ;
422
+ logger . EnqueueMessage ( _messageTwo ) ;
423
+ logger . EnqueueMessage ( _messageThree ) ;
424
+ // Pause for a bit before disposing so logger can finish logging
425
+ await WaitForFile ( fileName3 , _messageThree . Length ) . DefaultTimeout ( ) ;
426
+ }
427
+
428
+ // Even with a big enough FileSizeLimit, we still won't try to write to files from a previous instance.
429
+ options . FileSizeLimit = 10000 ;
430
+
431
+ await using ( var logger = new FileLoggerProcessor ( new OptionsWrapperMonitor < W3CLoggerOptions > ( options ) , new HostingEnvironment ( ) , NullLoggerFactory . Instance ) )
432
+ {
433
+ logger . EnqueueMessage ( _messageFour ) ;
434
+ // Pause for a bit before disposing so logger can finish logging
435
+ await WaitForFile ( fileName4 , _messageFour . Length ) . DefaultTimeout ( ) ;
436
+ }
437
+
438
+ var actualFiles = new DirectoryInfo ( path )
439
+ . GetFiles ( )
440
+ . Select ( f => f . Name )
441
+ . OrderBy ( f => f )
442
+ . ToArray ( ) ;
443
+
444
+ Assert . Equal ( 2 , actualFiles . Length ) ;
445
+
446
+ Assert . False ( File . Exists ( fileName1 ) ) ;
447
+ Assert . False ( File . Exists ( fileName2 ) ) ;
448
+ Assert . True ( File . Exists ( fileName3 ) ) ;
449
+ Assert . True ( File . Exists ( fileName4 ) ) ;
450
+
451
+ Assert . Equal ( _messageThree + Environment . NewLine , File . ReadAllText ( fileName3 ) ) ;
452
+ Assert . Equal ( _messageFour + Environment . NewLine , File . ReadAllText ( fileName4 ) ) ;
453
+ }
454
+ finally
455
+ {
456
+ Helpers . DisposeDirectory ( path ) ;
457
+ }
458
+ }
459
+
343
460
[ QuarantinedTest ( "https://github.com/dotnet/aspnetcore/issues/34982" ) ]
344
461
[ Fact ]
345
462
public async Task WritesToNewFileOnOptionsChange ( )
@@ -420,6 +537,14 @@ private async Task WaitForFile(string fileName, int length)
420
537
}
421
538
}
422
539
540
+ private async Task WaitForCondition ( Func < bool > waitForLog )
541
+ {
542
+ while ( ! waitForLog ( ) )
543
+ {
544
+ await Task . Delay ( 10 ) ;
545
+ }
546
+ }
547
+
423
548
private async Task WaitForRoll ( string fileName )
424
549
{
425
550
while ( File . Exists ( fileName ) )
0 commit comments