44using Serilog . Sinks . File . Tests . Support ;
55using Serilog . Configuration ;
66using Serilog . Core ;
7+ using Serilog . Debugging ;
8+ using Xunit . Abstractions ;
79
810namespace Serilog . Sinks . File . Tests ;
911
10- public class RollingFileSinkTests
12+ public class RollingFileSinkTests : IDisposable
1113{
14+ private readonly ITestOutputHelper _testOutputHelper ;
15+
16+ public RollingFileSinkTests ( ITestOutputHelper testOutputHelper )
17+ {
18+ _testOutputHelper = testOutputHelper ;
19+ }
20+
21+ public void Dispose ( )
22+ {
23+ SelfLog . Disable ( ) ;
24+ }
25+
1226 [ Fact ]
1327 public void LogEventsAreEmittedToTheFileNamedAccordingToTheEventTimestamp ( )
1428 {
@@ -145,6 +159,116 @@ public void WhenRetentionCountAndArchivingHookIsSetOldFilesAreCopiedAndOriginalD
145159 } ) ;
146160 }
147161
162+ [ Fact ]
163+ public void WhenFirstOpeningFailedWithLockRetryDelayedUntilNextCheckpoint ( )
164+ {
165+ var fileName = Some . String ( ) + ".txt" ;
166+ using var temp = new TempFolder ( ) ;
167+ using var log = new LoggerConfiguration ( )
168+ . WriteTo . File ( Path . Combine ( temp . Path , fileName ) , rollOnFileSizeLimit : true , fileSizeLimitBytes : 1 , rollingInterval : RollingInterval . Minute , hooks : new FailOpeningHook ( true , 2 , 3 , 4 ) )
169+ . CreateLogger ( ) ;
170+ LogEvent e1 = Some . InformationEvent ( new DateTime ( 2012 , 10 , 28 ) ) ,
171+ e2 = Some . InformationEvent ( e1 . Timestamp . AddSeconds ( 1 ) ) ,
172+ e3 = Some . InformationEvent ( e1 . Timestamp . AddMinutes ( 5 ) ) ,
173+ e4 = Some . InformationEvent ( e1 . Timestamp . AddMinutes ( 31 ) ) ;
174+ LogEvent [ ] logEvents = new [ ] { e1 , e2 , e3 , e4 } ;
175+
176+ foreach ( var logEvent in logEvents )
177+ {
178+ Clock . SetTestDateTimeNow ( logEvent . Timestamp . DateTime ) ;
179+ log . Write ( logEvent ) ;
180+ }
181+
182+ var files = Directory . GetFiles ( temp . Path )
183+ . OrderBy ( p => p , StringComparer . OrdinalIgnoreCase )
184+ . ToArray ( ) ;
185+ var pattern = "yyyyMMddHHmm" ;
186+
187+ Assert . Equal ( 6 , files . Length ) ;
188+ // Successful write of e1:
189+ Assert . True ( files [ 0 ] . EndsWith ( ExpectedFileName ( fileName , e1 . Timestamp , pattern ) ) , files [ 0 ] ) ;
190+ // Failing writes for e2, will be dropped and logged to SelfLog:
191+ Assert . True ( files [ 1 ] . EndsWith ( "_001.txt" ) , files [ 1 ] ) ;
192+ Assert . True ( files [ 2 ] . EndsWith ( "_002.txt" ) , files [ 2 ] ) ;
193+ Assert . True ( files [ 3 ] . EndsWith ( "_003.txt" ) , files [ 3 ] ) ;
194+ // Successful write of e3:
195+ Assert . True ( files [ 4 ] . EndsWith ( ExpectedFileName ( fileName , e3 . Timestamp , pattern ) ) , files [ 4 ] ) ;
196+ // Successful write of e4:
197+ Assert . True ( files [ 5 ] . EndsWith ( ExpectedFileName ( fileName , e4 . Timestamp , pattern ) ) , files [ 5 ] ) ;
198+ }
199+
200+ [ Fact ]
201+ public void WhenFirstOpeningFailedWithLockRetryDelayed30Minutes ( )
202+ {
203+ var fileName = Some . String ( ) + ".txt" ;
204+ using var temp = new TempFolder ( ) ;
205+ using var log = new LoggerConfiguration ( )
206+ . WriteTo . File ( Path . Combine ( temp . Path , fileName ) , rollOnFileSizeLimit : true , fileSizeLimitBytes : 1 , rollingInterval : RollingInterval . Hour , hooks : new FailOpeningHook ( true , 2 , 3 , 4 ) )
207+ . CreateLogger ( ) ;
208+ LogEvent e1 = Some . InformationEvent ( new DateTime ( 2012 , 10 , 28 ) ) ,
209+ e2 = Some . InformationEvent ( e1 . Timestamp . AddSeconds ( 1 ) ) ,
210+ e3 = Some . InformationEvent ( e1 . Timestamp . AddMinutes ( 5 ) ) ,
211+ e4 = Some . InformationEvent ( e1 . Timestamp . AddMinutes ( 31 ) ) ;
212+ LogEvent [ ] logEvents = new [ ] { e1 , e2 , e3 , e4 } ;
213+
214+ SelfLog . Enable ( _testOutputHelper . WriteLine ) ;
215+ foreach ( var logEvent in logEvents )
216+ {
217+ Clock . SetTestDateTimeNow ( logEvent . Timestamp . DateTime ) ;
218+ log . Write ( logEvent ) ;
219+ }
220+
221+ var files = Directory . GetFiles ( temp . Path )
222+ . OrderBy ( p => p , StringComparer . OrdinalIgnoreCase )
223+ . ToArray ( ) ;
224+ var pattern = "yyyyMMddHH" ;
225+
226+ Assert . Equal ( 4 , files . Length ) ;
227+ // Successful write of e1:
228+ Assert . True ( files [ 0 ] . EndsWith ( ExpectedFileName ( fileName , e1 . Timestamp , pattern ) ) , files [ 0 ] ) ;
229+ // Failing writes for e2, will be dropped and logged to SelfLog; on lock it will try it three times:
230+ Assert . True ( files [ 1 ] . EndsWith ( "_001.txt" ) , files [ 1 ] ) ;
231+ Assert . True ( files [ 2 ] . EndsWith ( "_002.txt" ) , files [ 2 ] ) ;
232+ /* e3 will be dropped and logged to SelfLog without new file as it's in the 30 minutes cooldown and roller only starts on next hour! */
233+ // Successful write of e4, the third file will be retried after failing initially:
234+ Assert . True ( files [ 3 ] . EndsWith ( "_003.txt" ) , files [ 3 ] ) ;
235+ }
236+
237+ [ Fact ]
238+ public void WhenFirstOpeningFailedWithoutLockRetryDelayed30Minutes ( )
239+ {
240+ var fileName = Some . String ( ) + ".txt" ;
241+ using var temp = new TempFolder ( ) ;
242+ using var log = new LoggerConfiguration ( )
243+ . WriteTo . File ( Path . Combine ( temp . Path , fileName ) , rollOnFileSizeLimit : true , fileSizeLimitBytes : 1 , rollingInterval : RollingInterval . Hour , hooks : new FailOpeningHook ( false , 2 ) )
244+ . CreateLogger ( ) ;
245+ LogEvent e1 = Some . InformationEvent ( new DateTime ( 2012 , 10 , 28 ) ) ,
246+ e2 = Some . InformationEvent ( e1 . Timestamp . AddSeconds ( 1 ) ) ,
247+ e3 = Some . InformationEvent ( e1 . Timestamp . AddMinutes ( 5 ) ) ,
248+ e4 = Some . InformationEvent ( e1 . Timestamp . AddMinutes ( 31 ) ) ;
249+ LogEvent [ ] logEvents = new [ ] { e1 , e2 , e3 , e4 } ;
250+
251+ SelfLog . Enable ( _testOutputHelper . WriteLine ) ;
252+ foreach ( var logEvent in logEvents )
253+ {
254+ Clock . SetTestDateTimeNow ( logEvent . Timestamp . DateTime ) ;
255+ log . Write ( logEvent ) ;
256+ }
257+
258+ var files = Directory . GetFiles ( temp . Path )
259+ . OrderBy ( p => p , StringComparer . OrdinalIgnoreCase )
260+ . ToArray ( ) ;
261+ var pattern = "yyyyMMddHH" ;
262+
263+ Assert . Equal ( 2 , files . Length ) ;
264+ // Successful write of e1:
265+ Assert . True ( files [ 0 ] . EndsWith ( ExpectedFileName ( fileName , e1 . Timestamp , pattern ) ) , files [ 0 ] ) ;
266+ /* Failing writes for e2, will be dropped and logged to SelfLog; on non-lock it will try it once */
267+ /* e3 will be dropped and logged to SelfLog without new file as it's in the 30 minutes cooldown and roller only starts on next hour! */
268+ // Successful write of e4, the file will be retried after failing initially:
269+ Assert . True ( files [ 1 ] . EndsWith ( "_001.txt" ) , files [ 1 ] ) ;
270+ }
271+
148272 [ Fact ]
149273 public void WhenSizeLimitIsBreachedNewFilesCreated ( )
150274 {
@@ -279,7 +403,7 @@ static void TestRollingEventSequence(
279403 Clock . SetTestDateTimeNow ( @event . Timestamp . DateTime ) ;
280404 log . Write ( @event ) ;
281405
282- var expected = pathFormat . Replace ( ".txt" , @event . Timestamp . ToString ( "yyyyMMdd" ) + ".txt ") ;
406+ var expected = ExpectedFileName ( pathFormat , @event . Timestamp , "yyyyMMdd ") ;
283407 Assert . True ( System . IO . File . Exists ( expected ) ) ;
284408
285409 verified . Add ( expected ) ;
@@ -292,4 +416,9 @@ static void TestRollingEventSequence(
292416 Directory . Delete ( folder , true ) ;
293417 }
294418 }
419+
420+ static string ExpectedFileName ( string fileName , DateTimeOffset timestamp , string pattern )
421+ {
422+ return fileName . Replace ( ".txt" , timestamp . ToString ( pattern ) + ".txt" ) ;
423+ }
295424}
0 commit comments