44using Serilog . Sinks . File . Tests . Support ;
55using Serilog . Configuration ;
66using Serilog . Core ;
7+ using Xunit . Abstractions ;
78
89namespace Serilog . Sinks . File . Tests ;
910
1011public class RollingFileSinkTests
1112{
13+ private readonly ITestOutputHelper _testOutputHelper ;
14+
15+ public RollingFileSinkTests ( ITestOutputHelper testOutputHelper )
16+ {
17+ _testOutputHelper = testOutputHelper ;
18+ }
19+
1220 [ Fact ]
1321 public void LogEventsAreEmittedToTheFileNamedAccordingToTheEventTimestamp ( )
1422 {
@@ -145,6 +153,130 @@ public void WhenRetentionCountAndArchivingHookIsSetOldFilesAreCopiedAndOriginalD
145153 } ) ;
146154 }
147155
156+ [ Fact ]
157+ public void WhenFirstOpeningFailedWithLockRetryDelayedUntilNextCheckpoint ( )
158+ {
159+ var fileName = Some . String ( ) + ".txt" ;
160+ using var temp = new TempFolder ( ) ;
161+ using var log = new LoggerConfiguration ( )
162+ . WriteTo . File ( Path . Combine ( temp . Path , fileName ) , rollOnFileSizeLimit : true , fileSizeLimitBytes : 1 , rollingInterval : RollingInterval . Minute , hooks : new FailOpeningHook ( true , 2 , 3 , 4 ) )
163+ . CreateLogger ( ) ;
164+ LogEvent e1 = Some . InformationEvent ( new DateTime ( 2012 , 10 , 28 ) ) ,
165+ e2 = Some . InformationEvent ( e1 . Timestamp . AddSeconds ( 1 ) ) ,
166+ e3 = Some . InformationEvent ( e1 . Timestamp . AddMinutes ( 5 ) ) ,
167+ e4 = Some . InformationEvent ( e1 . Timestamp . AddMinutes ( 31 ) ) ;
168+ LogEvent [ ] logEvents = new [ ] { e1 , e2 , e3 , e4 } ;
169+
170+ foreach ( var logEvent in logEvents )
171+ {
172+ Clock . SetTestDateTimeNow ( logEvent . Timestamp . DateTime ) ;
173+ log . Write ( logEvent ) ;
174+ }
175+
176+ var files = Directory . GetFiles ( temp . Path )
177+ . OrderBy ( p => p , StringComparer . OrdinalIgnoreCase )
178+ . ToArray ( ) ;
179+ var pattern = "yyyyMMddHHmm" ;
180+
181+ Assert . Equal ( 6 , files . Length ) ;
182+ // Successful write of e1:
183+ Assert . True ( files [ 0 ] . EndsWith ( ExpectedFileName ( fileName , e1 . Timestamp , pattern ) ) , files [ 0 ] ) ;
184+ // Failing writes for e2, will be dropped and logged to SelfLog:
185+ Assert . True ( files [ 1 ] . EndsWith ( "_001.txt" ) , files [ 1 ] ) ;
186+ Assert . True ( files [ 2 ] . EndsWith ( "_002.txt" ) , files [ 2 ] ) ;
187+ Assert . True ( files [ 3 ] . EndsWith ( "_003.txt" ) , files [ 3 ] ) ;
188+ // Successful write of e3:
189+ Assert . True ( files [ 4 ] . EndsWith ( ExpectedFileName ( fileName , e3 . Timestamp , pattern ) ) , files [ 4 ] ) ;
190+ // Successful write of e4:
191+ Assert . True ( files [ 5 ] . EndsWith ( ExpectedFileName ( fileName , e4 . Timestamp , pattern ) ) , files [ 5 ] ) ;
192+ }
193+
194+ [ Fact ]
195+ public void WhenFirstOpeningFailedWithLockRetryDelayed30Minutes ( )
196+ {
197+ var fileName = Some . String ( ) + ".txt" ;
198+ using var temp = new TempFolder ( ) ;
199+ LogEvent e1 = Some . InformationEvent ( new DateTime ( 2012 , 10 , 28 ) ) ,
200+ e2 = Some . InformationEvent ( e1 . Timestamp . AddSeconds ( 1 ) ) ,
201+ e3 = Some . InformationEvent ( e1 . Timestamp . AddMinutes ( 5 ) ) ,
202+ e4 = Some . InformationEvent ( e1 . Timestamp . AddMinutes ( 31 ) ) ;
203+ LogEvent [ ] logEvents = new [ ] { e1 , e2 , e3 , e4 } ;
204+
205+ using ( var log = new LoggerConfiguration ( )
206+ . WriteTo . File ( Path . Combine ( temp . Path , fileName ) , rollOnFileSizeLimit : true , fileSizeLimitBytes : 1 ,
207+ rollingInterval : RollingInterval . Hour , hooks : new FailOpeningHook ( true , 2 , 3 , 4 ) )
208+ . CreateLogger ( ) )
209+ {
210+ foreach ( var logEvent in logEvents )
211+ {
212+ Clock . SetTestDateTimeNow ( logEvent . Timestamp . DateTime ) ;
213+ log . Write ( logEvent ) ;
214+ }
215+ }
216+
217+ var files = Directory . GetFiles ( temp . Path )
218+ . OrderBy ( p => p , StringComparer . OrdinalIgnoreCase )
219+ . ToArray ( ) ;
220+ var pattern = "yyyyMMddHH" ;
221+
222+ foreach ( var file in files )
223+ {
224+ _testOutputHelper . WriteLine ( file + ": " + System . IO . File . ReadAllText ( file ) ) ;
225+ }
226+ Assert . Equal ( 5 , 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+ Assert . True ( files [ 3 ] . EndsWith ( "_003.txt" ) , files [ 3 ] ) ;
233+ /* 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! */
234+ // Successful write of e4:
235+ Assert . True ( files [ 4 ] . EndsWith ( "_004.txt" ) , files [ 4 ] ) ;
236+ }
237+
238+ [ Fact ]
239+ public void WhenFirstOpeningFailedWithoutLockRetryDelayed30Minutes ( )
240+ {
241+ var fileName = Some . String ( ) + ".txt" ;
242+ using var temp = new TempFolder ( ) ;
243+ LogEvent e1 = Some . InformationEvent ( new DateTime ( 2012 , 10 , 28 ) ) ,
244+ e2 = Some . InformationEvent ( e1 . Timestamp . AddSeconds ( 1 ) ) ,
245+ e3 = Some . InformationEvent ( e1 . Timestamp . AddMinutes ( 5 ) ) ,
246+ e4 = Some . InformationEvent ( e1 . Timestamp . AddMinutes ( 31 ) ) ;
247+ LogEvent [ ] logEvents = new [ ] { e1 , e2 , e3 , e4 } ;
248+
249+ using ( var log = new LoggerConfiguration ( )
250+ . WriteTo . File ( Path . Combine ( temp . Path , fileName ) , rollOnFileSizeLimit : true , fileSizeLimitBytes : 1 ,
251+ rollingInterval : RollingInterval . Hour , hooks : new FailOpeningHook ( false , 2 ) )
252+ . CreateLogger ( ) )
253+ {
254+ foreach ( var logEvent in logEvents )
255+ {
256+ Clock . SetTestDateTimeNow ( logEvent . Timestamp . DateTime ) ;
257+ log . Write ( logEvent ) ;
258+ }
259+ }
260+
261+ var files = Directory . GetFiles ( temp . Path )
262+ . OrderBy ( p => p , StringComparer . OrdinalIgnoreCase )
263+ . ToArray ( ) ;
264+ var pattern = "yyyyMMddHH" ;
265+
266+ foreach ( var file in files )
267+ {
268+ _testOutputHelper . WriteLine ( file + ": " + System . IO . File . ReadAllText ( file ) ) ;
269+ }
270+ Assert . Equal ( 3 , files . Length ) ;
271+ // Successful write of e1:
272+ Assert . True ( files [ 0 ] . EndsWith ( ExpectedFileName ( fileName , e1 . Timestamp , pattern ) ) , files [ 0 ] ) ;
273+ // Failing writes for e2, will be dropped and logged to SelfLog; on non-lock it will try it once:
274+ Assert . True ( files [ 1 ] . EndsWith ( "_001.txt" ) , files [ 1 ] ) ;
275+ /* 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! */
276+ // Successful write of e4:
277+ Assert . True ( files [ 2 ] . EndsWith ( "_002.txt" ) , files [ 2 ] ) ;
278+ }
279+
148280 [ Fact ]
149281 public void WhenSizeLimitIsBreachedNewFilesCreated ( )
150282 {
@@ -279,7 +411,7 @@ static void TestRollingEventSequence(
279411 Clock . SetTestDateTimeNow ( @event . Timestamp . DateTime ) ;
280412 log . Write ( @event ) ;
281413
282- var expected = pathFormat . Replace ( ".txt" , @event . Timestamp . ToString ( "yyyyMMdd" ) + ".txt ") ;
414+ var expected = ExpectedFileName ( pathFormat , @event . Timestamp , "yyyyMMdd ") ;
283415 Assert . True ( System . IO . File . Exists ( expected ) ) ;
284416
285417 verified . Add ( expected ) ;
@@ -292,4 +424,9 @@ static void TestRollingEventSequence(
292424 Directory . Delete ( folder , true ) ;
293425 }
294426 }
427+
428+ static string ExpectedFileName ( string fileName , DateTimeOffset timestamp , string pattern )
429+ {
430+ return fileName . Replace ( ".txt" , timestamp . ToString ( pattern ) + ".txt" ) ;
431+ }
295432}
0 commit comments