diff --git a/src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs b/src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs index 3518322..283f10e 100644 --- a/src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs +++ b/src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs @@ -71,7 +71,7 @@ public static LoggerConfiguration File( TimeSpan? flushToDiskInterval) { return File(sinkConfiguration, path, restrictedToMinimumLevel, outputTemplate, formatProvider, fileSizeLimitBytes, - levelSwitch, buffered, shared, flushToDiskInterval, RollingInterval.Infinite, false, null, null, null); + levelSwitch, buffered, shared, flushToDiskInterval, RollingInterval.Infinite, 1,false, null, null, null); } /// @@ -110,7 +110,7 @@ public static LoggerConfiguration File( TimeSpan? flushToDiskInterval) { return File(sinkConfiguration, formatter, path, restrictedToMinimumLevel, fileSizeLimitBytes, levelSwitch, - buffered, shared, flushToDiskInterval, RollingInterval.Infinite, false, null, null, null); + buffered, shared, flushToDiskInterval, RollingInterval.Infinite,1, false, null, null, null); } /// @@ -133,6 +133,7 @@ public static LoggerConfiguration File( /// Allow the log file to be shared by multiple processes. The default is false. /// If provided, a full disk flush will be performed periodically at the specified interval. /// The interval at which logging will roll over to a new file. + /// The number is multiplied by the interval. /// If true, a new file will be created when the file size limit is reached. Filenames /// will have a number appended in the format _NNN, with the first filename given no number. /// The maximum number of log files that will be retained, @@ -152,12 +153,13 @@ public static LoggerConfiguration File( bool shared, TimeSpan? flushToDiskInterval, RollingInterval rollingInterval, + int intervalMultiplier, bool rollOnFileSizeLimit, int? retainedFileCountLimit, Encoding encoding) { return File(sinkConfiguration, path, restrictedToMinimumLevel, outputTemplate, formatProvider, fileSizeLimitBytes, levelSwitch, buffered, - shared, flushToDiskInterval, rollingInterval, rollOnFileSizeLimit, retainedFileCountLimit, encoding, null); + shared, flushToDiskInterval, rollingInterval, intervalMultiplier,rollOnFileSizeLimit, retainedFileCountLimit, encoding, null); } /// @@ -166,7 +168,7 @@ public static LoggerConfiguration File( /// Logger sink configuration. /// A formatter, such as , to convert the log events into /// text for the file. If control of regular text formatting is required, use the other - /// overload of + /// overload of /// and specify the outputTemplate parameter instead. /// /// Path to the file. @@ -182,6 +184,7 @@ public static LoggerConfiguration File( /// Allow the log file to be shared by multiple processes. The default is false. /// If provided, a full disk flush will be performed periodically at the specified interval. /// The interval at which logging will roll over to a new file. + /// The number is multiplied by the interval. /// If true, a new file will be created when the file size limit is reached. Filenames /// will have a number appended in the format _NNN, with the first filename given no number. /// The maximum number of log files that will be retained, @@ -200,12 +203,13 @@ public static LoggerConfiguration File( bool shared, TimeSpan? flushToDiskInterval, RollingInterval rollingInterval, + int intervalMultiplier, bool rollOnFileSizeLimit, int? retainedFileCountLimit, Encoding encoding) { return File(sinkConfiguration, formatter, path, restrictedToMinimumLevel, fileSizeLimitBytes, levelSwitch, buffered, - shared, flushToDiskInterval, rollingInterval, rollOnFileSizeLimit, retainedFileCountLimit, encoding, null); + shared, flushToDiskInterval, rollingInterval, intervalMultiplier, rollOnFileSizeLimit, retainedFileCountLimit, encoding, null); } /// @@ -228,6 +232,7 @@ public static LoggerConfiguration File( /// Allow the log file to be shared by multiple processes. The default is false. /// If provided, a full disk flush will be performed periodically at the specified interval. /// The interval at which logging will roll over to a new file. + /// The number is multiplied by the interval. /// If true, a new file will be created when the file size limit is reached. Filenames /// will have a number appended in the format _NNN, with the first filename given no number. /// The maximum number of log files that will be retained, @@ -260,6 +265,7 @@ public static LoggerConfiguration File( bool shared = false, TimeSpan? flushToDiskInterval = null, RollingInterval rollingInterval = RollingInterval.Infinite, + int intervalMultiplier = 1, bool rollOnFileSizeLimit = false, int? retainedFileCountLimit = DefaultRetainedFileCountLimit, Encoding? encoding = null, @@ -273,7 +279,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, intervalMultiplier, rollOnFileSizeLimit, retainedFileCountLimit, encoding, hooks, retainedFileTimeLimit); } /// @@ -282,7 +288,7 @@ public static LoggerConfiguration File( /// Logger sink configuration. /// A formatter, such as , to convert the log events into /// text for the file. If control of regular text formatting is required, use the other - /// overload of + /// overload of /// and specify the outputTemplate parameter instead. /// /// Path to the file. @@ -298,6 +304,7 @@ public static LoggerConfiguration File( /// Allow the log file to be shared by multiple processes. The default is false. /// If provided, a full disk flush will be performed periodically at the specified interval. /// The interval at which logging will roll over to a new file. + /// The number is multiplied by the interval. /// If true, a new file will be created when the file size limit is reached. Filenames /// will have a number appended in the format _NNN, with the first filename given no number. /// The maximum number of log files that will be retained, @@ -329,6 +336,7 @@ public static LoggerConfiguration File( bool shared = false, TimeSpan? flushToDiskInterval = null, RollingInterval rollingInterval = RollingInterval.Infinite, + int intervalMultiplier = 1, bool rollOnFileSizeLimit = false, int? retainedFileCountLimit = DefaultRetainedFileCountLimit, Encoding? encoding = null, @@ -340,7 +348,7 @@ public static LoggerConfiguration File( if (path == null) throw new ArgumentNullException(nameof(path)); return ConfigureFile(sinkConfiguration.Sink, formatter, path, restrictedToMinimumLevel, fileSizeLimitBytes, levelSwitch, - buffered, false, shared, flushToDiskInterval, encoding, rollingInterval, rollOnFileSizeLimit, + buffered, false, shared, flushToDiskInterval, encoding, rollingInterval, intervalMultiplier, rollOnFileSizeLimit, retainedFileCountLimit, hooks, retainedFileTimeLimit); } @@ -496,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, 1, false, null, hooks, null); } static LoggerConfiguration ConfigureFile( @@ -512,6 +520,7 @@ static LoggerConfiguration ConfigureFile( TimeSpan? flushToDiskInterval, Encoding? encoding, RollingInterval rollingInterval, + int intervalMultiplier, bool rollOnFileSizeLimit, int? retainedFileCountLimit, FileLifecycleHooks? hooks, @@ -532,7 +541,7 @@ static LoggerConfiguration ConfigureFile( { 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, intervalMultiplier, rollOnFileSizeLimit, hooks, retainedFileTimeLimit); } else { diff --git a/src/Serilog.Sinks.File/Sinks/File/PathRoller.cs b/src/Serilog.Sinks.File/Sinks/File/PathRoller.cs index 79a6915..6e70e26 100644 --- a/src/Serilog.Sinks.File/Sinks/File/PathRoller.cs +++ b/src/Serilog.Sinks.File/Sinks/File/PathRoller.cs @@ -31,12 +31,14 @@ class PathRoller readonly Regex _filenameMatcher; readonly RollingInterval _interval; + private readonly int _intervalMultiplier; readonly string _periodFormat; - public PathRoller(string path, RollingInterval interval) + public PathRoller(string path, RollingInterval interval, int intervalMultiplier) { if (path == null) throw new ArgumentNullException(nameof(path)); _interval = interval; + _intervalMultiplier = intervalMultiplier; _periodFormat = interval.GetFormat(); var pathDirectory = Path.GetDirectoryName(path); @@ -112,6 +114,6 @@ public IEnumerable SelectMatches(IEnumerable filenames) public DateTime? GetCurrentCheckpoint(DateTime instant) => _interval.GetCurrentCheckpoint(instant); - public DateTime? GetNextCheckpoint(DateTime instant) => _interval.GetNextCheckpoint(instant); + public DateTime? GetNextCheckpoint(DateTime instant) => _interval.GetNextCheckpoint(_intervalMultiplier, instant); } } diff --git a/src/Serilog.Sinks.File/Sinks/File/RollingFileSink.cs b/src/Serilog.Sinks.File/Sinks/File/RollingFileSink.cs index dccb802..4497d14 100644 --- a/src/Serilog.Sinks.File/Sinks/File/RollingFileSink.cs +++ b/src/Serilog.Sinks.File/Sinks/File/RollingFileSink.cs @@ -50,6 +50,7 @@ public RollingFileSink(string path, bool buffered, bool shared, RollingInterval rollingInterval, + int intervalMultiplier, bool rollOnFileSizeLimit, FileLifecycleHooks? hooks, TimeSpan? retainedFileTimeLimit) @@ -58,8 +59,9 @@ public RollingFileSink(string path, if (fileSizeLimitBytes.HasValue && fileSizeLimitBytes < 1) throw new ArgumentException("Invalid value provided; file size limit must be at least 1 byte, or null."); if (retainedFileCountLimit.HasValue && retainedFileCountLimit < 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 (intervalMultiplier < 1) intervalMultiplier = 1; - _roller = new PathRoller(path, rollingInterval); + _roller = new PathRoller(path, rollingInterval, intervalMultiplier); _textFormatter = textFormatter; _fileSizeLimitBytes = fileSizeLimitBytes; _retainedFileCountLimit = retainedFileCountLimit; diff --git a/src/Serilog.Sinks.File/Sinks/File/RollingIntervalExtensions.cs b/src/Serilog.Sinks.File/Sinks/File/RollingIntervalExtensions.cs index 2c9e2fd..c38087a 100644 --- a/src/Serilog.Sinks.File/Sinks/File/RollingIntervalExtensions.cs +++ b/src/Serilog.Sinks.File/Sinks/File/RollingIntervalExtensions.cs @@ -60,7 +60,7 @@ public static string GetFormat(this RollingInterval interval) } } - public static DateTime? GetNextCheckpoint(this RollingInterval interval, DateTime instant) + public static DateTime? GetNextCheckpoint(this RollingInterval interval, int intervalMultiiplier, DateTime instant) { var current = GetCurrentCheckpoint(interval, instant); if (current == null) @@ -69,15 +69,15 @@ public static string GetFormat(this RollingInterval interval) switch (interval) { case RollingInterval.Year: - return current.Value.AddYears(1); + return current.Value.AddYears(intervalMultiiplier); case RollingInterval.Month: - return current.Value.AddMonths(1); + return current.Value.AddMonths(intervalMultiiplier); case RollingInterval.Day: - return current.Value.AddDays(1); + return current.Value.AddDays(intervalMultiiplier); case RollingInterval.Hour: - return current.Value.AddHours(1); + return current.Value.AddHours(intervalMultiiplier); case RollingInterval.Minute: - return current.Value.AddMinutes(1); + return current.Value.AddMinutes(intervalMultiiplier); default: throw new ArgumentException("Invalid rolling interval"); } diff --git a/test/Serilog.Sinks.File.Tests/RollingIntervalExtensionsTests.cs b/test/Serilog.Sinks.File.Tests/RollingIntervalExtensionsTests.cs index 404d5b4..e94e365 100644 --- a/test/Serilog.Sinks.File.Tests/RollingIntervalExtensionsTests.cs +++ b/test/Serilog.Sinks.File.Tests/RollingIntervalExtensionsTests.cs @@ -27,7 +27,7 @@ public void NextIntervalTests(RollingInterval interval, DateTime instant, DateTi var current = interval.GetCurrentCheckpoint(instant); Assert.Equal(currentCheckpoint, current); - var next = interval.GetNextCheckpoint(instant); + var next = interval.GetNextCheckpoint(1, instant); Assert.Equal(nextCheckpoint, next); } } diff --git a/test/Serilog.Sinks.File.Tests/TemplatedPathRollerTests.cs b/test/Serilog.Sinks.File.Tests/TemplatedPathRollerTests.cs index 65a974c..b65bc89 100644 --- a/test/Serilog.Sinks.File.Tests/TemplatedPathRollerTests.cs +++ b/test/Serilog.Sinks.File.Tests/TemplatedPathRollerTests.cs @@ -10,7 +10,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); @@ -19,7 +19,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); @@ -35,14 +35,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); @@ -51,7 +51,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); @@ -60,7 +60,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 }); @@ -70,7 +70,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); } @@ -79,7 +79,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); @@ -91,7 +91,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); }