diff --git a/src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs b/src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs
index 5440792..a114d72 100644
--- a/src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs
+++ b/src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs
@@ -164,7 +164,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.
@@ -236,6 +236,10 @@ public static LoggerConfiguration File(
/// Must be greater than or equal to .
/// Ignored if is .
/// The default is to retain files indefinitely.
+ /// Log file interval multiplier.
+ /// Ignored if is .
+ /// Must be at least 1, or null. If null then 1 used.
+ ///
/// Configuration object allowing method chaining.
/// When is null
/// When is null
@@ -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));
@@ -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);
}
///
@@ -280,7 +285,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.
@@ -306,6 +311,10 @@ public static LoggerConfiguration File(
/// Must be greater than or equal to .
/// Ignored if is .
/// The default is to retain files indefinitely.
+ /// Log file interval multiplier.
+ /// Ignored if is .
+ /// Must be at least 1, or null. If null then 1 used.
+ ///
/// Configuration object allowing method chaining.
/// When is null
/// When is null
@@ -331,7 +340,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 (formatter == null) throw new ArgumentNullException(nameof(formatter));
@@ -339,7 +349,7 @@ public static LoggerConfiguration File(
return ConfigureFile(sinkConfiguration.Sink, formatter, path, restrictedToMinimumLevel, fileSizeLimitBytes, levelSwitch,
buffered, false, shared, flushToDiskInterval, encoding, rollingInterval, rollOnFileSizeLimit,
- retainedFileCountLimit, hooks, retainedFileTimeLimit);
+ retainedFileCountLimit, hooks, retainedFileTimeLimit, rollingIntervalDuration);
}
///
@@ -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(
@@ -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));
@@ -523,6 +534,7 @@ 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;
@@ -530,7 +542,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, rollOnFileSizeLimit, hooks, retainedFileTimeLimit, rollingIntervalDuration);
}
else
{
diff --git a/src/Serilog.Sinks.File/Sinks/File/PathRoller.cs b/src/Serilog.Sinks.File/Sinks/File/PathRoller.cs
index e6773eb..dc64fca 100644
--- a/src/Serilog.Sinks.File/Sinks/File/PathRoller.cs
+++ b/src/Serilog.Sinks.File/Sinks/File/PathRoller.cs
@@ -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.
@@ -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);
@@ -109,5 +113,5 @@ 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(instant, _intervalDuration);
}
diff --git a/src/Serilog.Sinks.File/Sinks/File/RollingFileSink.cs b/src/Serilog.Sinks.File/Sinks/File/RollingFileSink.cs
index 6c55d44..f084cf2 100644
--- a/src/Serilog.Sinks.File/Sinks/File/RollingFileSink.cs
+++ b/src/Serilog.Sinks.File/Sinks/File/RollingFileSink.cs
@@ -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;
diff --git a/src/Serilog.Sinks.File/Sinks/File/RollingIntervalExtensions.cs b/src/Serilog.Sinks.File/Sinks/File/RollingIntervalExtensions.cs
index a469abe..5973358 100644
--- a/src/Serilog.Sinks.File/Sinks/File/RollingIntervalExtensions.cs
+++ b/src/Serilog.Sinks.File/Sinks/File/RollingIntervalExtensions.cs
@@ -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.
@@ -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.")
};
}
diff --git a/test/Serilog.Sinks.File.Tests/TemplatedPathRollerTests.cs b/test/Serilog.Sinks.File.Tests/TemplatedPathRollerTests.cs
index e34c335..c0e1224 100644
--- a/test/Serilog.Sinks.File.Tests/TemplatedPathRollerTests.cs
+++ b/test/Serilog.Sinks.File.Tests/TemplatedPathRollerTests.cs
@@ -1,4 +1,4 @@
-using Xunit;
+using Xunit;
namespace Serilog.Sinks.File.Tests;
@@ -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);
@@ -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);
@@ -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);
@@ -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);
@@ -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 });
@@ -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);
}
@@ -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);
@@ -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);
}