14
14
15
15
using System ;
16
16
using System . ComponentModel ;
17
+ using System . Text ;
17
18
using Serilog . Configuration ;
18
19
using Serilog . Core ;
19
20
using Serilog . Debugging ;
@@ -30,8 +31,9 @@ namespace Serilog
30
31
/// <summary>Extends <see cref="LoggerConfiguration"/> with methods to add file sinks.</summary>
31
32
public static class FileLoggerConfigurationExtensions
32
33
{
34
+ const int DefaultRetainedFileCountLimit = 31 ; // A long month of logs
33
35
const long DefaultFileSizeLimitBytes = 1L * 1024 * 1024 * 1024 ;
34
- const string DefaultOutputTemplate = "[ {Timestamp:o} {Level:u3}] {Message:lj} {Properties }{NewLine}{Exception}" ;
36
+ const string DefaultOutputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [ {Level:u3}] {Message:lj}{NewLine}{Exception}" ;
35
37
36
38
/// <summary>
37
39
/// Write log events to the specified file.
@@ -44,7 +46,7 @@ public static class FileLoggerConfigurationExtensions
44
46
/// to be changed at runtime.</param>
45
47
/// <param name="formatProvider">Supplies culture-specific formatting information, or null.</param>
46
48
/// <param name="outputTemplate">A message template describing the format used to write to the sink.
47
- /// the default is "[ {Timestamp:o} {Level:u3}] {Message:lj} {Properties }{NewLine}{Exception}".</param>
49
+ /// the default is "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [ {Level:u3}] {Message:lj}{NewLine}{Exception}".</param>
48
50
/// <param name="fileSizeLimitBytes">The approximate maximum size, in bytes, to which a log file will be allowed to grow.
49
51
/// For unrestricted growth, pass null. The default is 1 GB. To avoid writing partial events, the last event within the limit
50
52
/// will be written in full even if it exceeds the limit.</param>
@@ -124,7 +126,7 @@ public static LoggerConfiguration File(
124
126
/// to be changed at runtime.</param>
125
127
/// <param name="formatProvider">Supplies culture-specific formatting information, or null.</param>
126
128
/// <param name="outputTemplate">A message template describing the format used to write to the sink.
127
- /// the default is "[ {Timestamp:o} {Level:u3}] {Message:lj} {Properties }{NewLine}{Exception}".</param>
129
+ /// the default is "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [ {Level:u3}] {Message:lj}{NewLine}{Exception}".</param>
128
130
/// <param name="fileSizeLimitBytes">The approximate maximum size, in bytes, to which a log file will be allowed to grow.
129
131
/// For unrestricted growth, pass null. The default is 1 GB. To avoid writing partial events, the last event within the limit
130
132
/// will be written in full even if it exceeds the limit.</param>
@@ -135,6 +137,9 @@ public static LoggerConfiguration File(
135
137
/// <param name="rollingInterval">The interval at which logging will roll over to a new file.</param>
136
138
/// <param name="rollOnFileSizeLimit">If <code>true</code>, a new file will be created when the file size limit is reached. Filenames
137
139
/// will have a number appended in the format <code>_NNNNN</code>, with the first filename given no number.</param>
140
+ /// <param name="retainedFileCountLimit">The maximum number of log files that will be retained,
141
+ /// including the current log file. For unlimited retention, pass null. The default is 31.</param>
142
+ /// <param name="encoding">Character encoding used to write the text file. The default is UTF-8 without BOM.</param>
138
143
/// <returns>Configuration object allowing method chaining.</returns>
139
144
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
140
145
public static LoggerConfiguration File (
@@ -149,7 +154,9 @@ public static LoggerConfiguration File(
149
154
bool shared = false ,
150
155
TimeSpan ? flushToDiskInterval = null ,
151
156
RollingInterval rollingInterval = RollingInterval . Infinite ,
152
- bool rollOnFileSizeLimit = false )
157
+ bool rollOnFileSizeLimit = false ,
158
+ int ? retainedFileCountLimit = DefaultRetainedFileCountLimit ,
159
+ Encoding encoding = null )
153
160
{
154
161
if ( sinkConfiguration == null ) throw new ArgumentNullException ( nameof ( sinkConfiguration ) ) ;
155
162
if ( path == null ) throw new ArgumentNullException ( nameof ( path ) ) ;
@@ -165,7 +172,7 @@ public static LoggerConfiguration File(
165
172
/// <param name="sinkConfiguration">Logger sink configuration.</param>
166
173
/// <param name="formatter">A formatter, such as <see cref="JsonFormatter"/>, to convert the log events into
167
174
/// text for the file. If control of regular text formatting is required, use the other
168
- /// overload of <see cref="File(LoggerSinkConfiguration, string, LogEventLevel, string, IFormatProvider, long?, LoggingLevelSwitch, bool, bool, TimeSpan?, RollingInterval, bool)"/>
175
+ /// overload of <see cref="File(LoggerSinkConfiguration, string, LogEventLevel, string, IFormatProvider, long?, LoggingLevelSwitch, bool, bool, TimeSpan?, RollingInterval, bool, int?, Encoding )"/>
169
176
/// and specify the outputTemplate parameter instead.
170
177
/// </param>
171
178
/// <param name="path">Path to the file.</param>
@@ -183,6 +190,9 @@ public static LoggerConfiguration File(
183
190
/// <param name="rollingInterval">The interval at which logging will roll over to a new file.</param>
184
191
/// <param name="rollOnFileSizeLimit">If <code>true</code>, a new file will be created when the file size limit is reached. Filenames
185
192
/// will have a number appended in the format <code>_NNNNN</code>, with the first filename given no number.</param>
193
+ /// <param name="retainedFileCountLimit">The maximum number of log files that will be retained,
194
+ /// including the current log file. For unlimited retention, pass null. The default is 31.</param>
195
+ /// <param name="encoding">Character encoding used to write the text file. The default is UTF-8 without BOM.</param>
186
196
/// <returns>Configuration object allowing method chaining.</returns>
187
197
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
188
198
public static LoggerConfiguration File (
@@ -196,9 +206,13 @@ public static LoggerConfiguration File(
196
206
bool shared = false ,
197
207
TimeSpan ? flushToDiskInterval = null ,
198
208
RollingInterval rollingInterval = RollingInterval . Infinite ,
199
- bool rollOnFileSizeLimit = false )
209
+ bool rollOnFileSizeLimit = false ,
210
+ int ? retainedFileCountLimit = DefaultRetainedFileCountLimit ,
211
+ Encoding encoding = null )
200
212
{
201
- return ConfigureFile ( sinkConfiguration . Sink , formatter , path , restrictedToMinimumLevel , fileSizeLimitBytes , levelSwitch , buffered : buffered , shared : shared , flushToDiskInterval : flushToDiskInterval , rollingInterval : rollingInterval , rollOnFileSizeLimit : rollOnFileSizeLimit ) ;
213
+ return ConfigureFile ( sinkConfiguration . Sink , formatter , path , restrictedToMinimumLevel , fileSizeLimitBytes , levelSwitch ,
214
+ buffered : buffered , shared : shared , flushToDiskInterval : flushToDiskInterval , rollingInterval : rollingInterval ,
215
+ rollOnFileSizeLimit : rollOnFileSizeLimit , retainedFileCountLimit : retainedFileCountLimit , encoding : encoding ) ;
202
216
}
203
217
204
218
/// <summary>
@@ -212,7 +226,7 @@ public static LoggerConfiguration File(
212
226
/// to be changed at runtime.</param>
213
227
/// <param name="formatProvider">Supplies culture-specific formatting information, or null.</param>
214
228
/// <param name="outputTemplate">A message template describing the format used to write to the sink.
215
- /// the default is "[ {Timestamp:o} {Level:u3}] {Message:lj} {Properties }{NewLine}{Exception}".</param>
229
+ /// the default is "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [ {Level:u3}] {Message:lj}{NewLine}{Exception}".</param>
216
230
/// <returns>Configuration object allowing method chaining.</returns>
217
231
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
218
232
public static LoggerConfiguration File (
@@ -268,36 +282,48 @@ static LoggerConfiguration ConfigureFile(
268
282
bool propagateExceptions = false ,
269
283
bool shared = false ,
270
284
TimeSpan ? flushToDiskInterval = null ,
285
+ Encoding encoding = null ,
271
286
RollingInterval rollingInterval = RollingInterval . Infinite ,
272
- bool rollOnFileSizeLimit = false )
287
+ bool rollOnFileSizeLimit = false ,
288
+ int ? retainedFileCountLimit = DefaultRetainedFileCountLimit )
273
289
{
274
290
if ( addSink == null ) throw new ArgumentNullException ( nameof ( addSink ) ) ;
275
291
if ( formatter == null ) throw new ArgumentNullException ( nameof ( formatter ) ) ;
276
292
if ( path == null ) throw new ArgumentNullException ( nameof ( path ) ) ;
277
- if ( fileSizeLimitBytes . HasValue && fileSizeLimitBytes < 0 ) throw new ArgumentException ( "Negative value provided; file size limit must be non-negative" ) ;
278
- if ( shared && buffered )
279
- throw new ArgumentException ( "Buffered writes are not available when file sharing is enabled." , nameof ( buffered ) ) ;
293
+ if ( fileSizeLimitBytes . HasValue && fileSizeLimitBytes < 0 ) throw new ArgumentException ( "Negative value provided; file size limit must be non-negative." , nameof ( fileSizeLimitBytes ) ) ;
294
+ if ( retainedFileCountLimit . HasValue && retainedFileCountLimit < 1 ) throw new ArgumentException ( "At least one file must be retained." , nameof ( retainedFileCountLimit ) ) ;
295
+ if ( shared && buffered ) throw new ArgumentException ( "Buffered writes are not available when file sharing is enabled." , nameof ( buffered ) ) ;
280
296
281
297
ILogEventSink sink ;
282
- try
298
+
299
+ if ( rollOnFileSizeLimit || rollingInterval != RollingInterval . Infinite )
300
+ {
301
+ sink = new RollingFileSink ( path , formatter , fileSizeLimitBytes , retainedFileCountLimit , encoding , buffered , shared , rollingInterval , rollOnFileSizeLimit ) ;
302
+ }
303
+ else
283
304
{
284
- if ( shared )
305
+ try
285
306
{
286
- sink = new SharedFileSink ( path , formatter , fileSizeLimitBytes ) ;
307
+ #pragma warning disable 618
308
+ if ( shared )
309
+ {
310
+ sink = new SharedFileSink ( path , formatter , fileSizeLimitBytes ) ;
311
+ }
312
+ else
313
+ {
314
+ sink = new FileSink ( path , formatter , fileSizeLimitBytes , buffered : buffered ) ;
315
+ }
316
+ #pragma warning restore 618
287
317
}
288
- else
318
+ catch ( Exception ex )
289
319
{
290
- sink = new FileSink ( path , formatter , fileSizeLimitBytes , buffered : buffered ) ;
291
- }
292
- }
293
- catch ( Exception ex )
294
- {
295
- SelfLog . WriteLine ( "Unable to open file sink for {0}: {1}" , path , ex ) ;
320
+ SelfLog . WriteLine ( "Unable to open file sink for {0}: {1}" , path , ex ) ;
296
321
297
- if ( propagateExceptions )
298
- throw ;
322
+ if ( propagateExceptions )
323
+ throw ;
299
324
300
- return addSink ( new NullSink ( ) , LevelAlias . Maximum , null ) ;
325
+ return addSink ( new NullSink ( ) , LevelAlias . Maximum , null ) ;
326
+ }
301
327
}
302
328
303
329
if ( flushToDiskInterval . HasValue )
0 commit comments