1
1
using System ;
2
+ using System . Collections . Generic ;
2
3
using System . IO ;
3
4
using System . IO . Compression ;
5
+ using System . Linq ;
4
6
using Serilog . Debugging ;
5
7
6
8
namespace Serilog . Sinks . File . Archive
@@ -13,6 +15,7 @@ namespace Serilog.Sinks.File.Archive
13
15
public class ArchiveHooks : FileLifecycleHooks
14
16
{
15
17
private readonly CompressionLevel compressionLevel ;
18
+ private readonly int retainedFileCountLimit ;
16
19
private readonly string targetDirectory ;
17
20
18
21
/// <summary>
@@ -27,12 +30,26 @@ public class ArchiveHooks : FileLifecycleHooks
27
30
public ArchiveHooks ( CompressionLevel compressionLevel = CompressionLevel . Fastest , string targetDirectory = null )
28
31
{
29
32
if ( compressionLevel == CompressionLevel . NoCompression && targetDirectory == null )
30
- throw new ArgumentException ( "Either compressionLevel or targetDirectory must be set" ) ;
33
+ throw new ArgumentException ( $ "Either { nameof ( compressionLevel ) } or { nameof ( targetDirectory ) } must be set") ;
31
34
32
35
this . compressionLevel = compressionLevel ;
33
36
this . targetDirectory = targetDirectory ;
34
37
}
35
38
39
+ public ArchiveHooks ( int retainedFileCountLimit , CompressionLevel compressionLevel = CompressionLevel . Fastest , string targetDirectory = null )
40
+ {
41
+ if ( retainedFileCountLimit <= 0 )
42
+ throw new ArgumentException ( $ "{ nameof ( retainedFileCountLimit ) } must be greater than zero", nameof ( retainedFileCountLimit ) ) ;
43
+ if ( targetDirectory is not null && TokenExpander . IsTokenised ( targetDirectory ) )
44
+ throw new ArgumentException ( $ "{ nameof ( targetDirectory ) } must not be tokenised when using { nameof ( retainedFileCountLimit ) } ", nameof ( targetDirectory ) ) ;
45
+ if ( compressionLevel == CompressionLevel . NoCompression )
46
+ throw new ArgumentException ( $ "{ nameof ( compressionLevel ) } must not be 'NoCompression' when using { nameof ( retainedFileCountLimit ) } ", nameof ( compressionLevel ) ) ;
47
+
48
+ this . compressionLevel = compressionLevel ;
49
+ this . retainedFileCountLimit = retainedFileCountLimit ;
50
+ this . targetDirectory = null ;
51
+ }
52
+
36
53
public override void OnFileDeleting ( string path )
37
54
{
38
55
try
@@ -70,12 +87,62 @@ public override void OnFileDeleting(string path)
70
87
sourceStream . CopyTo ( compressStream ) ;
71
88
}
72
89
}
90
+ //only apply archive file limit if we are archiving to a non tokenised path (constant path)
91
+ if ( this . retainedFileCountLimit > 0 && ! this . IsArchivePathTokenised )
92
+ {
93
+ RemoveExcessFiles ( currentTargetDir ) ;
94
+ }
73
95
}
74
96
catch ( Exception ex )
75
97
{
76
98
SelfLog . WriteLine ( "Error while archiving file {0}: {1}" , path , ex ) ;
77
99
throw ;
78
100
}
79
101
}
102
+
103
+ private bool IsArchivePathTokenised => this . targetDirectory is not null && TokenExpander . IsTokenised ( this . targetDirectory ) ;
104
+
105
+ private void RemoveExcessFiles ( string folder )
106
+ {
107
+ var searchPattern = this . compressionLevel != CompressionLevel . NoCompression
108
+ ? "*.gz"
109
+ : "*.*" ;
110
+
111
+ var filesToDelete = Directory . GetFiles ( folder , searchPattern )
112
+ . Select ( f => new FileInfo ( f ) )
113
+ . OrderByDescending ( f => f , LogFileComparer . Default )
114
+ . Skip ( this . retainedFileCountLimit )
115
+ . ToList ( ) ;
116
+ foreach ( var file in filesToDelete )
117
+ {
118
+ try
119
+ {
120
+ file . Delete ( ) ;
121
+ }
122
+ catch ( Exception ex )
123
+ {
124
+ SelfLog . WriteLine ( "Error while deleting file {0}: {1}" , file . FullName , ex ) ;
125
+ }
126
+ }
127
+ }
128
+
129
+ private class LogFileComparer : IComparer < FileInfo >
130
+ {
131
+ public static IComparer < FileInfo > Default = new LogFileComparer ( ) ;
132
+
133
+ //This will not work correctly when the file uses a date format where lexicographical order does not correspond to chronological order but frankly, if you
134
+ //are using non ISO 8601 date formats in your files you should be shot.
135
+ //It would be best if the file sink could expose a way to sort files chronologically because I think LastWriteTime is probably not determisitic enough.
136
+ public int Compare ( FileInfo x , FileInfo y )
137
+ {
138
+ if ( x is null && y is null )
139
+ return 0 ;
140
+ if ( x is null )
141
+ return - 1 ;
142
+ if ( y is null )
143
+ return 1 ;
144
+ return string . Compare ( x . Name , y . Name , StringComparison . OrdinalIgnoreCase ) ;
145
+ }
146
+ }
80
147
}
81
148
}
0 commit comments