Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added rollingInterval multiplier #323

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 21 additions & 9 deletions src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ public static LoggerConfiguration File(
/// <param name="sinkConfiguration">Logger sink configuration.</param>
/// <param name="formatter">A formatter, such as <see cref="JsonFormatter"/>, to convert the log events into
/// text for the file. If control of regular text formatting is required, use the other
/// overload of <see cref="File(LoggerSinkConfiguration, string, LogEventLevel, string, IFormatProvider, long?, LoggingLevelSwitch, bool, bool, TimeSpan?, RollingInterval, bool, int?, Encoding, FileLifecycleHooks, TimeSpan?)"/>
/// overload of <see cref="File(LoggerSinkConfiguration, string, LogEventLevel, string, IFormatProvider, long?, LoggingLevelSwitch, bool, bool, TimeSpan?, RollingInterval, bool, int?, Encoding, FileLifecycleHooks, TimeSpan?, int?)"/>
/// and specify the outputTemplate parameter instead.
/// </param>
/// <param name="path">Path to the file.</param>
Expand Down Expand Up @@ -236,6 +236,10 @@ public static LoggerConfiguration File(
/// Must be greater than or equal to <see cref="TimeSpan.Zero"/>.
/// Ignored if <paramref see="rollingInterval"/> is <see cref="RollingInterval.Infinite"/>.
/// The default is to retain files indefinitely.</param>
/// <param name="rollingIntervalDuration">Log file interval multiplier.
/// Ignored if <paramref see="rollingInterval"/> is <see cref="RollingInterval.Infinite"/>.
/// Must be at least 1, or null. If null then 1 used.
/// </param>
/// <returns>Configuration object allowing method chaining.</returns>
/// <exception cref="ArgumentNullException">When <paramref name="sinkConfiguration"/> is <code>null</code></exception>
/// <exception cref="ArgumentNullException">When <paramref name="path"/> is <code>null</code></exception>
Expand All @@ -262,7 +266,8 @@ public static LoggerConfiguration File(
int? retainedFileCountLimit = DefaultRetainedFileCountLimit,
Encoding? encoding = null,
FileLifecycleHooks? hooks = null,
TimeSpan? retainedFileTimeLimit = null)
TimeSpan? retainedFileTimeLimit = null,
int? rollingIntervalDuration = null)
{
if (sinkConfiguration == null) throw new ArgumentNullException(nameof(sinkConfiguration));
if (path == null) throw new ArgumentNullException(nameof(path));
Expand All @@ -271,7 +276,7 @@ public static LoggerConfiguration File(
var formatter = new MessageTemplateTextFormatter(outputTemplate, formatProvider);
return File(sinkConfiguration, formatter, path, restrictedToMinimumLevel, fileSizeLimitBytes,
levelSwitch, buffered, shared, flushToDiskInterval,
rollingInterval, rollOnFileSizeLimit, retainedFileCountLimit, encoding, hooks, retainedFileTimeLimit);
rollingInterval, rollOnFileSizeLimit, retainedFileCountLimit, encoding, hooks, retainedFileTimeLimit, rollingIntervalDuration);
}

/// <summary>
Expand All @@ -280,7 +285,7 @@ public static LoggerConfiguration File(
/// <param name="sinkConfiguration">Logger sink configuration.</param>
/// <param name="formatter">A formatter, such as <see cref="JsonFormatter"/>, to convert the log events into
/// text for the file. If control of regular text formatting is required, use the other
/// overload of <see cref="File(LoggerSinkConfiguration, string, LogEventLevel, string, IFormatProvider, long?, LoggingLevelSwitch, bool, bool, TimeSpan?, RollingInterval, bool, int?, Encoding, FileLifecycleHooks, TimeSpan?)"/>
/// overload of <see cref="File(LoggerSinkConfiguration, string, LogEventLevel, string, IFormatProvider, long?, LoggingLevelSwitch, bool, bool, TimeSpan?, RollingInterval, bool, int?, Encoding, FileLifecycleHooks, TimeSpan?, int?)"/>
/// and specify the outputTemplate parameter instead.
/// </param>
/// <param name="path">Path to the file.</param>
Expand All @@ -306,6 +311,10 @@ public static LoggerConfiguration File(
/// Must be greater than or equal to <see cref="TimeSpan.Zero"/>.
/// Ignored if <paramref see="rollingInterval"/> is <see cref="RollingInterval.Infinite"/>.
/// The default is to retain files indefinitely.</param>
/// <param name="rollingIntervalDuration">Log file interval multiplier.
/// Ignored if <paramref see="rollingInterval"/> is <see cref="RollingInterval.Infinite"/>.
/// Must be at least 1, or null. If null then 1 used.
/// </param>
/// <returns>Configuration object allowing method chaining.</returns>
/// <exception cref="ArgumentNullException">When <paramref name="sinkConfiguration"/> is <code>null</code></exception>
/// <exception cref="ArgumentNullException">When <paramref name="formatter"/> is <code>null</code></exception>
Expand All @@ -331,15 +340,16 @@ public static LoggerConfiguration File(
int? retainedFileCountLimit = DefaultRetainedFileCountLimit,
Encoding? encoding = null,
FileLifecycleHooks? hooks = null,
TimeSpan? retainedFileTimeLimit = null)
TimeSpan? retainedFileTimeLimit = null,
int? rollingIntervalDuration = null)
{
if (sinkConfiguration == null) throw new ArgumentNullException(nameof(sinkConfiguration));
if (formatter == null) throw new ArgumentNullException(nameof(formatter));
if (path == null) throw new ArgumentNullException(nameof(path));

return ConfigureFile(sinkConfiguration.Sink, formatter, path, restrictedToMinimumLevel, fileSizeLimitBytes, levelSwitch,
buffered, false, shared, flushToDiskInterval, encoding, rollingInterval, rollOnFileSizeLimit,
retainedFileCountLimit, hooks, retainedFileTimeLimit);
retainedFileCountLimit, hooks, retainedFileTimeLimit, rollingIntervalDuration);
}

/// <summary>
Expand Down Expand Up @@ -494,7 +504,7 @@ public static LoggerConfiguration File(
if (path == null) throw new ArgumentNullException(nameof(path));

return ConfigureFile(sinkConfiguration.Sink, formatter, path, restrictedToMinimumLevel, null, levelSwitch, false, true,
false, null, encoding, RollingInterval.Infinite, false, null, hooks, null);
false, null, encoding, RollingInterval.Infinite, false, null, hooks, null, null);
}

static LoggerConfiguration ConfigureFile(
Expand All @@ -513,7 +523,8 @@ static LoggerConfiguration ConfigureFile(
bool rollOnFileSizeLimit,
int? retainedFileCountLimit,
FileLifecycleHooks? hooks,
TimeSpan? retainedFileTimeLimit)
TimeSpan? retainedFileTimeLimit,
int? rollingIntervalDuration)
{
if (addSink == null) throw new ArgumentNullException(nameof(addSink));
if (formatter == null) throw new ArgumentNullException(nameof(formatter));
Expand All @@ -523,14 +534,15 @@ static LoggerConfiguration ConfigureFile(
if (retainedFileTimeLimit.HasValue && retainedFileTimeLimit < TimeSpan.Zero) throw new ArgumentException("Negative value provided; retained file time limit must be non-negative.", nameof(retainedFileTimeLimit));
if (shared && buffered) throw new ArgumentException("Buffered writes are not available when file sharing is enabled.", nameof(buffered));
if (shared && hooks != null) throw new ArgumentException("File lifecycle hooks are not currently supported for shared log files.", nameof(hooks));
if ((rollOnFileSizeLimit || rollingInterval != RollingInterval.Infinite) && rollingIntervalDuration.HasValue && rollingIntervalDuration < 1) throw new ArgumentException("Zero or negative value provided; rolling interval duration must be at least 1, or null.", nameof(rollingIntervalDuration));

ILogEventSink sink;

try
{
if (rollOnFileSizeLimit || rollingInterval != RollingInterval.Infinite)
{
sink = new RollingFileSink(path, formatter, fileSizeLimitBytes, retainedFileCountLimit, encoding, buffered, shared, rollingInterval, rollOnFileSizeLimit, hooks, retainedFileTimeLimit);
sink = new RollingFileSink(path, formatter, fileSizeLimitBytes, retainedFileCountLimit, encoding, buffered, shared, rollingInterval, rollOnFileSizeLimit, hooks, retainedFileTimeLimit, rollingIntervalDuration);
}
else
{
Expand Down
10 changes: 7 additions & 3 deletions src/Serilog.Sinks.File/Sinks/File/PathRoller.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2013-2016 Serilog Contributors
// Copyright 2013-2016 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,12 +28,16 @@ sealed class PathRoller
readonly Regex _filenameMatcher;

readonly RollingInterval _interval;
private readonly int _intervalDuration;
readonly string _periodFormat;

public PathRoller(string path, RollingInterval interval)
public PathRoller(string path, RollingInterval interval, int intervalDuration)
{
if (path == null) throw new ArgumentNullException(nameof(path));
if (interval != RollingInterval.Infinite && intervalDuration < 1) throw new ArgumentException(nameof(intervalDuration));

_interval = interval;
_intervalDuration = intervalDuration;
_periodFormat = interval.GetFormat();

var pathDirectory = Path.GetDirectoryName(path);
Expand Down Expand Up @@ -109,5 +113,5 @@ public IEnumerable<RollingLogFile> SelectMatches(IEnumerable<string> filenames)

public DateTime? GetCurrentCheckpoint(DateTime instant) => _interval.GetCurrentCheckpoint(instant);

public DateTime? GetNextCheckpoint(DateTime instant) => _interval.GetNextCheckpoint(instant);
public DateTime? GetNextCheckpoint(DateTime instant) => _interval.GetNextCheckpoint(instant, _intervalDuration);
}
6 changes: 4 additions & 2 deletions src/Serilog.Sinks.File/Sinks/File/RollingFileSink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,16 @@ public RollingFileSink(string path,
RollingInterval rollingInterval,
bool rollOnFileSizeLimit,
FileLifecycleHooks? hooks,
TimeSpan? retainedFileTimeLimit)
TimeSpan? retainedFileTimeLimit,
int? rollingIntervalDuration)
{
if (path == null) throw new ArgumentNullException(nameof(path));
if (fileSizeLimitBytes is < 1) throw new ArgumentException("Invalid value provided; file size limit must be at least 1 byte, or null.");
if (retainedFileCountLimit is < 1) throw new ArgumentException("Zero or negative value provided; retained file count limit must be at least 1.");
if (retainedFileTimeLimit.HasValue && retainedFileTimeLimit < TimeSpan.Zero) throw new ArgumentException("Negative value provided; retained file time limit must be non-negative.", nameof(retainedFileTimeLimit));
if (rollingInterval != RollingInterval.Infinite && rollingIntervalDuration.HasValue && rollingIntervalDuration < 1) throw new ArgumentException("Zero or negative value provided; rolling interval duration must be at least 1.", nameof(rollingIntervalDuration));

_roller = new PathRoller(path, rollingInterval);
_roller = new PathRoller(path, rollingInterval, rollingIntervalDuration ?? 1);
_textFormatter = textFormatter;
_fileSizeLimitBytes = fileSizeLimitBytes;
_retainedFileCountLimit = retainedFileCountLimit;
Expand Down
17 changes: 10 additions & 7 deletions src/Serilog.Sinks.File/Sinks/File/RollingIntervalExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2017 Serilog Contributors
// Copyright 2017 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -44,19 +44,22 @@ public static string GetFormat(this RollingInterval interval)
};
}

public static DateTime? GetNextCheckpoint(this RollingInterval interval, DateTime instant)
public static DateTime? GetNextCheckpoint(this RollingInterval interval, DateTime instant, int? intervalDuration = null)
{
var current = GetCurrentCheckpoint(interval, instant);
if (current == null)
return null;

if (!intervalDuration.HasValue || intervalDuration < 1)
intervalDuration = 1;

return interval switch
{
RollingInterval.Year => current.Value.AddYears(1),
RollingInterval.Month => current.Value.AddMonths(1),
RollingInterval.Day => current.Value.AddDays(1),
RollingInterval.Hour => current.Value.AddHours(1),
RollingInterval.Minute => current.Value.AddMinutes(1),
RollingInterval.Year => current.Value.AddYears(intervalDuration.Value),
RollingInterval.Month => current.Value.AddMonths(intervalDuration.Value),
RollingInterval.Day => current.Value.AddDays(intervalDuration.Value),
RollingInterval.Hour => current.Value.AddHours(intervalDuration.Value),
RollingInterval.Minute => current.Value.AddMinutes(intervalDuration.Value),
_ => throw new ArgumentException("Invalid rolling interval.")
};
}
Expand Down
20 changes: 10 additions & 10 deletions test/Serilog.Sinks.File.Tests/TemplatedPathRollerTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Xunit;
using Xunit;

namespace Serilog.Sinks.File.Tests;

Expand All @@ -7,7 +7,7 @@ public class PathRollerTests
[Fact]
public void TheLogFileIncludesDateToken()
{
var roller = new PathRoller(Path.Combine("Logs", "log-.txt"), RollingInterval.Day);
var roller = new PathRoller(Path.Combine("Logs", "log-.txt"), RollingInterval.Day, 1);
var now = new DateTime(2013, 7, 14, 3, 24, 9, 980);
roller.GetLogFilePath(now, null, out var path);
AssertEqualAbsolute(Path.Combine("Logs", "log-20130714.txt"), path);
Expand All @@ -16,7 +16,7 @@ public void TheLogFileIncludesDateToken()
[Fact]
public void ANonZeroIncrementIsIncludedAndPadded()
{
var roller = new PathRoller(Path.Combine("Logs", "log-.txt"), RollingInterval.Day);
var roller = new PathRoller(Path.Combine("Logs", "log-.txt"), RollingInterval.Day, 1);
var now = new DateTime(2013, 7, 14, 3, 24, 9, 980);
roller.GetLogFilePath(now, 12, out var path);
AssertEqualAbsolute(Path.Combine("Logs", "log-20130714_012.txt"), path);
Expand All @@ -32,14 +32,14 @@ static void AssertEqualAbsolute(string path1, string path2)
[Fact]
public void TheRollerReturnsTheLogFileDirectory()
{
var roller = new PathRoller(Path.Combine("Logs", "log-.txt"), RollingInterval.Day);
var roller = new PathRoller(Path.Combine("Logs", "log-.txt"), RollingInterval.Day, 1);
AssertEqualAbsolute("Logs", roller.LogFileDirectory);
}

[Fact]
public void TheLogFileIsNotRequiredToIncludeAnExtension()
{
var roller = new PathRoller(Path.Combine("Logs", "log-"), RollingInterval.Day);
var roller = new PathRoller(Path.Combine("Logs", "log-"), RollingInterval.Day, 1);
var now = new DateTime(2013, 7, 14, 3, 24, 9, 980);
roller.GetLogFilePath(now, null, out var path);
AssertEqualAbsolute(Path.Combine("Logs", "log-20130714"), path);
Expand All @@ -48,7 +48,7 @@ public void TheLogFileIsNotRequiredToIncludeAnExtension()
[Fact]
public void TheLogFileIsNotRequiredToIncludeADirectory()
{
var roller = new PathRoller("log-", RollingInterval.Day);
var roller = new PathRoller("log-", RollingInterval.Day, 1);
var now = new DateTime(2013, 7, 14, 3, 24, 9, 980);
roller.GetLogFilePath(now, null, out var path);
AssertEqualAbsolute("log-20130714", path);
Expand All @@ -57,7 +57,7 @@ public void TheLogFileIsNotRequiredToIncludeADirectory()
[Fact]
public void MatchingExcludesSimilarButNonMatchingFiles()
{
var roller = new PathRoller("log-.txt", RollingInterval.Day);
var roller = new PathRoller("log-.txt", RollingInterval.Day, 1);
const string similar1 = "log-0.txt";
const string similar2 = "log-hello.txt";
var matched = roller.SelectMatches(new[] { similar1, similar2 });
Expand All @@ -67,7 +67,7 @@ public void MatchingExcludesSimilarButNonMatchingFiles()
[Fact]
public void TheDirectorSearchPatternUsesWildcardInPlaceOfDate()
{
var roller = new PathRoller(Path.Combine("Logs", "log-.txt"), RollingInterval.Day);
var roller = new PathRoller(Path.Combine("Logs", "log-.txt"), RollingInterval.Day, 1);
Assert.Equal("log-*.txt", roller.DirectorySearchPattern);
}

Expand All @@ -76,7 +76,7 @@ public void TheDirectorSearchPatternUsesWildcardInPlaceOfDate()
[InlineData("log-.txt", "log-2013121013.txt", "log-2013121013_031.txt", RollingInterval.Hour)]
public void MatchingSelectsFiles(string template, string zeroth, string thirtyFirst, RollingInterval interval)
{
var roller = new PathRoller(template, interval);
var roller = new PathRoller(template, interval, 1);
var matched = roller.SelectMatches(new[] { zeroth, thirtyFirst }).ToArray();
Assert.Equal(2, matched.Length);
Assert.Null(matched[0].SequenceNumber);
Expand All @@ -88,7 +88,7 @@ public void MatchingSelectsFiles(string template, string zeroth, string thirtyFi
[InlineData("log-.txt", "log-2015010110.txt", "log-2015010109.txt", RollingInterval.Hour)]
public void MatchingParsesSubstitutions(string template, string newer, string older, RollingInterval interval)
{
var roller = new PathRoller(template, interval);
var roller = new PathRoller(template, interval, 1);
var matched = roller.SelectMatches(new[] { older, newer }).OrderByDescending(m => m.DateTime).Select(m => m.Filename).ToArray();
Assert.Equal(new[] { newer, older }, matched);
}
Expand Down