Skip to content

Commit a015038

Browse files
committed
Merge branch 'thiagosgarcia-dev' into dev
2 parents 1093c3b + c2375eb commit a015038

File tree

6 files changed

+119
-37
lines changed

6 files changed

+119
-37
lines changed

appveyor.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
version: '{build}'
22
skip_tags: true
33
image:
4-
- Visual Studio 2017
5-
- Ubuntu
6-
configuration: Release
4+
- Visual Studio 2019
5+
- Ubuntu1804
76
build_script:
87
- ps: ./Build.ps1
98
for:
109
-
1110
matrix:
1211
only:
13-
- image: Ubuntu
12+
- image: Ubuntu1804
1413
build_script:
1514
- sh build.sh
1615
test: off

serilog-sinks-file.sln.DotSettings

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
2+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Serilog/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ public static LoggerConfiguration File(
165165
/// <param name="sinkConfiguration">Logger sink configuration.</param>
166166
/// <param name="formatter">A formatter, such as <see cref="JsonFormatter"/>, to convert the log events into
167167
/// 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, int?, Encoding, FileLifecycleHooks)"/>
168+
/// overload of <see cref="File(LoggerSinkConfiguration, string, LogEventLevel, string, IFormatProvider, long?, LoggingLevelSwitch, bool, bool, TimeSpan?, RollingInterval, bool, int?, Encoding, FileLifecycleHooks, TimeSpan?)"/>
169169
/// and specify the outputTemplate parameter instead.
170170
/// </param>
171171
/// <param name="path">Path to the file.</param>
@@ -181,7 +181,7 @@ public static LoggerConfiguration File(
181181
/// <param name="shared">Allow the log file to be shared by multiple processes. The default is false.</param>
182182
/// <param name="flushToDiskInterval">If provided, a full disk flush will be performed periodically at the specified interval.</param>
183183
/// <param name="rollingInterval">The interval at which logging will roll over to a new file.</param>
184-
/// <param name="rollOnFileSizeLimit">If <code>true</code>, a new file will be created when the file size limit is reached. Filenames
184+
/// <param name="rollOnFileSizeLimit">If <code>true</code>, a new file will be created when the file size limit is reached. Filenames
185185
/// will have a number appended in the format <code>_NNN</code>, with the first filename given no number.</param>
186186
/// <param name="retainedFileCountLimit">The maximum number of log files that will be retained,
187187
/// including the current log file. For unlimited retention, pass null. The default is 31.</param>
@@ -227,12 +227,16 @@ public static LoggerConfiguration File(
227227
/// <param name="shared">Allow the log file to be shared by multiple processes. The default is false.</param>
228228
/// <param name="flushToDiskInterval">If provided, a full disk flush will be performed periodically at the specified interval.</param>
229229
/// <param name="rollingInterval">The interval at which logging will roll over to a new file.</param>
230-
/// <param name="rollOnFileSizeLimit">If <code>true</code>, a new file will be created when the file size limit is reached. Filenames
230+
/// <param name="rollOnFileSizeLimit">If <code>true</code>, a new file will be created when the file size limit is reached. Filenames
231231
/// will have a number appended in the format <code>_NNN</code>, with the first filename given no number.</param>
232232
/// <param name="retainedFileCountLimit">The maximum number of log files that will be retained,
233233
/// including the current log file. For unlimited retention, pass null. The default is 31.</param>
234234
/// <param name="encoding">Character encoding used to write the text file. The default is UTF-8 without BOM.</param>
235235
/// <param name="hooks">Optionally enables hooking into log file lifecycle events.</param>
236+
/// <param name="retainedFileTimeLimit">The maximum time after the end of an interval that a rolling log file will be retained.
237+
/// Must be greater than or equal to <see cref="TimeSpan.Zero"/>.
238+
/// Ignored if <paramref see="rollingInterval"/> is <see cref="RollingInterval.Infinite"/>.
239+
/// The default is to retain files indefinitely.</param>
236240
/// <returns>Configuration object allowing method chaining.</returns>
237241
public static LoggerConfiguration File(
238242
this LoggerSinkConfiguration sinkConfiguration,
@@ -249,7 +253,8 @@ public static LoggerConfiguration File(
249253
bool rollOnFileSizeLimit = false,
250254
int? retainedFileCountLimit = DefaultRetainedFileCountLimit,
251255
Encoding encoding = null,
252-
FileLifecycleHooks hooks = null)
256+
FileLifecycleHooks hooks = null,
257+
TimeSpan? retainedFileTimeLimit = null)
253258
{
254259
if (sinkConfiguration == null) throw new ArgumentNullException(nameof(sinkConfiguration));
255260
if (path == null) throw new ArgumentNullException(nameof(path));
@@ -258,7 +263,7 @@ public static LoggerConfiguration File(
258263
var formatter = new MessageTemplateTextFormatter(outputTemplate, formatProvider);
259264
return File(sinkConfiguration, formatter, path, restrictedToMinimumLevel, fileSizeLimitBytes,
260265
levelSwitch, buffered, shared, flushToDiskInterval,
261-
rollingInterval, rollOnFileSizeLimit, retainedFileCountLimit, encoding, hooks);
266+
rollingInterval, rollOnFileSizeLimit, retainedFileCountLimit, encoding, hooks, retainedFileTimeLimit);
262267
}
263268

264269
/// <summary>
@@ -267,7 +272,7 @@ public static LoggerConfiguration File(
267272
/// <param name="sinkConfiguration">Logger sink configuration.</param>
268273
/// <param name="formatter">A formatter, such as <see cref="JsonFormatter"/>, to convert the log events into
269274
/// text for the file. If control of regular text formatting is required, use the other
270-
/// overload of <see cref="File(LoggerSinkConfiguration, string, LogEventLevel, string, IFormatProvider, long?, LoggingLevelSwitch, bool, bool, TimeSpan?, RollingInterval, bool, int?, Encoding, FileLifecycleHooks)"/>
275+
/// overload of <see cref="File(LoggerSinkConfiguration, string, LogEventLevel, string, IFormatProvider, long?, LoggingLevelSwitch, bool, bool, TimeSpan?, RollingInterval, bool, int?, Encoding, FileLifecycleHooks, TimeSpan?)"/>
271276
/// and specify the outputTemplate parameter instead.
272277
/// </param>
273278
/// <param name="path">Path to the file.</param>
@@ -289,6 +294,10 @@ public static LoggerConfiguration File(
289294
/// including the current log file. For unlimited retention, pass null. The default is 31.</param>
290295
/// <param name="encoding">Character encoding used to write the text file. The default is UTF-8 without BOM.</param>
291296
/// <param name="hooks">Optionally enables hooking into log file lifecycle events.</param>
297+
/// <param name="retainedFileTimeLimit">The maximum time after the end of an interval that a rolling log file will be retained.
298+
/// Must be greater than or equal to <see cref="TimeSpan.Zero"/>.
299+
/// Ignored if <paramref see="rollingInterval"/> is <see cref="RollingInterval.Infinite"/>.
300+
/// The default is to retain files indefinitely.</param>
292301
/// <returns>Configuration object allowing method chaining.</returns>
293302
public static LoggerConfiguration File(
294303
this LoggerSinkConfiguration sinkConfiguration,
@@ -304,15 +313,16 @@ public static LoggerConfiguration File(
304313
bool rollOnFileSizeLimit = false,
305314
int? retainedFileCountLimit = DefaultRetainedFileCountLimit,
306315
Encoding encoding = null,
307-
FileLifecycleHooks hooks = null)
316+
FileLifecycleHooks hooks = null,
317+
TimeSpan? retainedFileTimeLimit = null)
308318
{
309319
if (sinkConfiguration == null) throw new ArgumentNullException(nameof(sinkConfiguration));
310320
if (formatter == null) throw new ArgumentNullException(nameof(formatter));
311321
if (path == null) throw new ArgumentNullException(nameof(path));
312322

313323
return ConfigureFile(sinkConfiguration.Sink, formatter, path, restrictedToMinimumLevel, fileSizeLimitBytes, levelSwitch,
314324
buffered, false, shared, flushToDiskInterval, encoding, rollingInterval, rollOnFileSizeLimit,
315-
retainedFileCountLimit, hooks);
325+
retainedFileCountLimit, hooks, retainedFileTimeLimit);
316326
}
317327

318328
/// <summary>
@@ -432,7 +442,7 @@ public static LoggerConfiguration File(
432442
if (path == null) throw new ArgumentNullException(nameof(path));
433443

434444
return ConfigureFile(sinkConfiguration.Sink, formatter, path, restrictedToMinimumLevel, null, levelSwitch, false, true,
435-
false, null, encoding, RollingInterval.Infinite, false, null, hooks);
445+
false, null, encoding, RollingInterval.Infinite, false, null, hooks, null);
436446
}
437447

438448
static LoggerConfiguration ConfigureFile(
@@ -450,21 +460,23 @@ static LoggerConfiguration ConfigureFile(
450460
RollingInterval rollingInterval,
451461
bool rollOnFileSizeLimit,
452462
int? retainedFileCountLimit,
453-
FileLifecycleHooks hooks)
463+
FileLifecycleHooks hooks,
464+
TimeSpan? retainedFileTimeLimit)
454465
{
455466
if (addSink == null) throw new ArgumentNullException(nameof(addSink));
456467
if (formatter == null) throw new ArgumentNullException(nameof(formatter));
457468
if (path == null) throw new ArgumentNullException(nameof(path));
458469
if (fileSizeLimitBytes.HasValue && fileSizeLimitBytes < 0) throw new ArgumentException("Negative value provided; file size limit must be non-negative.", nameof(fileSizeLimitBytes));
459470
if (retainedFileCountLimit.HasValue && retainedFileCountLimit < 1) throw new ArgumentException("At least one file must be retained.", nameof(retainedFileCountLimit));
471+
if (retainedFileTimeLimit.HasValue && retainedFileTimeLimit < TimeSpan.Zero) throw new ArgumentException("Negative value provided; retained file time limit must be non-negative.", nameof(retainedFileTimeLimit));
460472
if (shared && buffered) throw new ArgumentException("Buffered writes are not available when file sharing is enabled.", nameof(buffered));
461473
if (shared && hooks != null) throw new ArgumentException("File lifecycle hooks are not currently supported for shared log files.", nameof(hooks));
462474

463475
ILogEventSink sink;
464476

465477
if (rollOnFileSizeLimit || rollingInterval != RollingInterval.Infinite)
466478
{
467-
sink = new RollingFileSink(path, formatter, fileSizeLimitBytes, retainedFileCountLimit, encoding, buffered, shared, rollingInterval, rollOnFileSizeLimit, hooks);
479+
sink = new RollingFileSink(path, formatter, fileSizeLimitBytes, retainedFileCountLimit, encoding, buffered, shared, rollingInterval, rollOnFileSizeLimit, hooks, retainedFileTimeLimit);
468480
}
469481
else
470482
{

src/Serilog.Sinks.File/Serilog.Sinks.File.csproj

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@
66
<Authors>Serilog Contributors</Authors>
77
<TargetFrameworks>net45;netstandard1.3;netstandard2.0</TargetFrameworks>
88
<GenerateDocumentationFile>true</GenerateDocumentationFile>
9-
<AssemblyName>Serilog.Sinks.File</AssemblyName>
109
<AssemblyOriginatorKeyFile>../../assets/Serilog.snk</AssemblyOriginatorKeyFile>
1110
<SignAssembly>true</SignAssembly>
1211
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
13-
<PackageId>Serilog.Sinks.File</PackageId>
1412
<PackageTags>serilog;file</PackageTags>
1513
<PackageIconUrl>http://serilog.net/images/serilog-sink-nuget.png</PackageIconUrl>
1614
<PackageProjectUrl>http://serilog.net</PackageProjectUrl>
@@ -20,10 +18,7 @@
2018
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
2119
<RootNamespace>Serilog</RootNamespace>
2220
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
23-
<AssemblyName>Serilog.Sinks.File</AssemblyName>
24-
<!-- Don't reference the full NETStandard.Library -->
2521
<DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>
26-
<!-- SourceLink configuration -->
2722
<EnableSourceLink Condition="'$(EnableSourceLink)' == ''">false</EnableSourceLink>
2823
<PublishRepositoryUrl>true</PublishRepositoryUrl>
2924
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>

src/Serilog.Sinks.File/Sinks/File/RollingFileSink.cs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ sealed class RollingFileSink : ILogEventSink, IFlushableFileSink, IDisposable
2929
readonly ITextFormatter _textFormatter;
3030
readonly long? _fileSizeLimitBytes;
3131
readonly int? _retainedFileCountLimit;
32+
readonly TimeSpan? _retainedFileTimeLimit;
3233
readonly Encoding _encoding;
3334
readonly bool _buffered;
3435
readonly bool _shared;
@@ -50,16 +51,19 @@ public RollingFileSink(string path,
5051
bool shared,
5152
RollingInterval rollingInterval,
5253
bool rollOnFileSizeLimit,
53-
FileLifecycleHooks hooks)
54+
FileLifecycleHooks hooks,
55+
TimeSpan? retainedFileTimeLimit)
5456
{
5557
if (path == null) throw new ArgumentNullException(nameof(path));
56-
if (fileSizeLimitBytes.HasValue && fileSizeLimitBytes < 0) throw new ArgumentException("Negative value provided; file size limit must be non-negative.");
57-
if (retainedFileCountLimit.HasValue && retainedFileCountLimit < 1) throw new ArgumentException("Zero or negative value provided; retained file count limit must be at least 1.");
58+
if (fileSizeLimitBytes.HasValue && fileSizeLimitBytes < 0) throw new ArgumentException("Negative value provided; file size limit must be non-negative");
59+
if (retainedFileCountLimit.HasValue && retainedFileCountLimit < 1) throw new ArgumentException("Zero or negative value provided; retained file count limit must be at least 1");
60+
if (retainedFileTimeLimit.HasValue && retainedFileTimeLimit < TimeSpan.Zero) throw new ArgumentException("Negative value provided; retained file time limit must be non-negative.", nameof(retainedFileTimeLimit));
5861

5962
_roller = new PathRoller(path, rollingInterval);
6063
_textFormatter = textFormatter;
6164
_fileSizeLimitBytes = fileSizeLimitBytes;
6265
_retainedFileCountLimit = retainedFileCountLimit;
66+
_retainedFileTimeLimit = retainedFileTimeLimit;
6367
_encoding = encoding;
6468
_buffered = buffered;
6569
_shared = shared;
@@ -166,32 +170,32 @@ void OpenFile(DateTime now, int? minSequence = null)
166170
throw;
167171
}
168172

169-
ApplyRetentionPolicy(path);
173+
ApplyRetentionPolicy(path, now);
170174
return;
171175
}
172176
}
173177

174-
void ApplyRetentionPolicy(string currentFilePath)
178+
void ApplyRetentionPolicy(string currentFilePath, DateTime now)
175179
{
176-
if (_retainedFileCountLimit == null) return;
180+
if (_retainedFileCountLimit == null && _retainedFileTimeLimit == null) return;
177181

178182
var currentFileName = Path.GetFileName(currentFilePath);
179183

180184
// We consider the current file to exist, even if nothing's been written yet,
181185
// because files are only opened on response to an event being processed.
182186
var potentialMatches = Directory.GetFiles(_roller.LogFileDirectory, _roller.DirectorySearchPattern)
183187
.Select(Path.GetFileName)
184-
.Union(new [] { currentFileName });
188+
.Union(new[] { currentFileName });
185189

186190
var newestFirst = _roller
187191
.SelectMatches(potentialMatches)
188192
.OrderByDescending(m => m.DateTime)
189-
.ThenByDescending(m => m.SequenceNumber)
190-
.Select(m => m.Filename);
193+
.ThenByDescending(m => m.SequenceNumber);
191194

192195
var toRemove = newestFirst
193-
.Where(n => StringComparer.OrdinalIgnoreCase.Compare(currentFileName, n) != 0)
194-
.Skip(_retainedFileCountLimit.Value - 1)
196+
.Where(n => StringComparer.OrdinalIgnoreCase.Compare(currentFileName, n.Filename) != 0)
197+
.SkipWhile((f, i) => ShouldRetainFile(f, i, now))
198+
.Select(x => x.Filename)
195199
.ToList();
196200

197201
foreach (var obsolete in toRemove)
@@ -209,6 +213,20 @@ void ApplyRetentionPolicy(string currentFilePath)
209213
}
210214
}
211215

216+
bool ShouldRetainFile(RollingLogFile file, int index, DateTime now)
217+
{
218+
if (_retainedFileCountLimit.HasValue && index >= _retainedFileCountLimit.Value - 1)
219+
return false;
220+
221+
if (_retainedFileTimeLimit.HasValue && file.DateTime.HasValue &&
222+
file.DateTime.Value < now.Subtract(_retainedFileTimeLimit.Value))
223+
{
224+
return false;
225+
}
226+
227+
return true;
228+
}
229+
212230
public void Dispose()
213231
{
214232
lock (_syncRoot)

0 commit comments

Comments
 (0)