From e1255a0b296eaebc393bb978e0f2dbe9bb3dd579 Mon Sep 17 00:00:00 2001 From: Ramaz Date: Tue, 11 Mar 2025 01:40:52 +0400 Subject: [PATCH 1/2] Added support for customising date in filename by including {Date} placeholder. This commit simply adds support to include date at the start (instead of the end) of filename whenever needed. Relevant Tests were also added For PathRoller.cs in TemplatedPathRollerTests.cs This commit partially addresses #243 --- .../Sinks/File/PathRoller.cs | 54 +++++++++--- .../TemplatedPathRollerTests.cs | 82 ++++++++++++++++++- 2 files changed, 124 insertions(+), 12 deletions(-) diff --git a/src/Serilog.Sinks.File/Sinks/File/PathRoller.cs b/src/Serilog.Sinks.File/Sinks/File/PathRoller.cs index e6773eb..5cff740 100644 --- a/src/Serilog.Sinks.File/Sinks/File/PathRoller.cs +++ b/src/Serilog.Sinks.File/Sinks/File/PathRoller.cs @@ -43,16 +43,9 @@ public PathRoller(string path, RollingInterval interval) _directory = Path.GetFullPath(pathDirectory); _filenamePrefix = Path.GetFileNameWithoutExtension(path); _filenameSuffix = Path.GetExtension(path); - _filenameMatcher = new Regex( - "^" + - Regex.Escape(_filenamePrefix) + - "(?<" + PeriodMatchGroup + ">\\d{" + _periodFormat.Length + "})" + - "(?<" + SequenceNumberMatchGroup + ">_[0-9]{3,}){0,1}" + - Regex.Escape(_filenameSuffix) + - "$", - RegexOptions.Compiled); + _filenameMatcher = GetFileNameMatcher(); - DirectorySearchPattern = $"{_filenamePrefix}*{_filenameSuffix}"; + DirectorySearchPattern = GetDirectorySearchPattern(); } public string LogFileDirectory => _directory; @@ -68,7 +61,12 @@ public void GetLogFilePath(DateTime date, int? sequenceNumber, out string path) if (sequenceNumber != null) tok += "_" + sequenceNumber.Value.ToString("000", CultureInfo.InvariantCulture); - path = Path.Combine(_directory, _filenamePrefix + tok + _filenameSuffix); + string fileNameReplaced = ReplaceDatePlaceholder(_filenamePrefix, tok); + + if (string.Equals(_filenamePrefix, fileNameReplaced, StringComparison.InvariantCultureIgnoreCase)) + fileNameReplaced += tok; + + path = Path.Combine(_directory, fileNameReplaced + _filenameSuffix); } public IEnumerable SelectMatches(IEnumerable filenames) @@ -110,4 +108,40 @@ public IEnumerable SelectMatches(IEnumerable filenames) public DateTime? GetCurrentCheckpoint(DateTime instant) => _interval.GetCurrentCheckpoint(instant); public DateTime? GetNextCheckpoint(DateTime instant) => _interval.GetNextCheckpoint(instant); + + Regex GetFileNameMatcher() + { + if (_filenamePrefix.StartsWith("{Date}", StringComparison.InvariantCultureIgnoreCase)) + { + return new Regex( + "^" + + "(?<" + PeriodMatchGroup + ">\\d{" + _periodFormat.Length + "})" + + "(?<" + SequenceNumberMatchGroup + ">_[0-9]{3,}){0,1}" + + Regex.Escape(ReplaceDatePlaceholder(_filenamePrefix, string.Empty)) + + Regex.Escape(_filenameSuffix) + + "$", + RegexOptions.Compiled); + } + + return new Regex( + "^" + + Regex.Escape(_filenamePrefix) + + "(?<" + PeriodMatchGroup + ">\\d{" + _periodFormat.Length + "})" + + "(?<" + SequenceNumberMatchGroup + ">_[0-9]{3,}){0,1}" + + Regex.Escape(_filenameSuffix) + + "$", + RegexOptions.Compiled); + } + + string GetDirectorySearchPattern() + { + return _filenamePrefix.StartsWith("{Date}", StringComparison.InvariantCultureIgnoreCase) + ? $"*{ReplaceDatePlaceholder(_filenamePrefix, string.Empty)}{_filenameSuffix}" + : $"{_filenamePrefix}*{_filenameSuffix}"; + } + + static string ReplaceDatePlaceholder(string filenamePrefix, string dateString) + { + return Regex.Replace(filenamePrefix, @"\{Date}", dateString, RegexOptions.IgnoreCase); + } } diff --git a/test/Serilog.Sinks.File.Tests/TemplatedPathRollerTests.cs b/test/Serilog.Sinks.File.Tests/TemplatedPathRollerTests.cs index e34c335..7ff029c 100644 --- a/test/Serilog.Sinks.File.Tests/TemplatedPathRollerTests.cs +++ b/test/Serilog.Sinks.File.Tests/TemplatedPathRollerTests.cs @@ -1,9 +1,42 @@ -using Xunit; +using System.Globalization; +using Xunit; namespace Serilog.Sinks.File.Tests; public class PathRollerTests { + [Theory] + [InlineData(null)] + [InlineData(2)] + public void GetLogFilePath_WhenPathContainsDateTokenAtStart_ShouldReplaceDateToken(int? sequenceNumber) + { + var now = new DateTime(2013, 7, 14, 3, 24, 9, 980); + string expectedName = sequenceNumber is null + ? $"{now:yyyyMMdd}-logs.txt" + : $"{now:yyyyMMdd}_{sequenceNumber.Value.ToString("000", CultureInfo.InvariantCulture)}-logs.txt"; + + var roller = new PathRoller(Path.Combine("Logs", "{date}-logs.txt"), RollingInterval.Day); + roller.GetLogFilePath(now, sequenceNumber, out string actualName); + + AssertEqualAbsolute(Path.Combine("Logs", expectedName), actualName); + } + + [Theory] + [InlineData(null)] + [InlineData(2)] + public void GetLogFilePath_WhenPathContainsDateTokenAtEnd_ShouldReplaceDateToken(int? sequenceNumber) + { + var now = new DateTime(2013, 7, 14, 3, 24, 9, 980); + string expectedName = sequenceNumber is null + ? $"logs-{now:yyyyMMdd}.txt" + : $"logs-{now:yyyyMMdd}_{sequenceNumber.Value.ToString("000", CultureInfo.InvariantCulture)}.txt"; + + var roller = new PathRoller(Path.Combine("Logs", "logs-{Date}.txt"), RollingInterval.Day); + roller.GetLogFilePath(now, sequenceNumber, out string actualName); + + AssertEqualAbsolute(Path.Combine("Logs", expectedName), actualName); + } + [Fact] public void TheLogFileIncludesDateToken() { @@ -22,6 +55,15 @@ public void ANonZeroIncrementIsIncludedAndPadded() AssertEqualAbsolute(Path.Combine("Logs", "log-20130714_012.txt"), path); } + [Fact] + public void ANonZeroIncrementIsIncludedAndPaddedWhenDatePlaceHolderExists() + { + var roller = new PathRoller(Path.Combine("Logs", "{Date}-log.txt"), RollingInterval.Day); + var now = new DateTime(2013, 7, 14, 3, 24, 9, 980); + roller.GetLogFilePath(now, 12, out var path); + AssertEqualAbsolute(Path.Combine("Logs", "20130714_012-log.txt"), path); + } + static void AssertEqualAbsolute(string path1, string path2) { var abs1 = Path.GetFullPath(path1); @@ -36,6 +78,13 @@ public void TheRollerReturnsTheLogFileDirectory() AssertEqualAbsolute("Logs", roller.LogFileDirectory); } + [Fact] + public void TheRollerReturnsTheLogFileDirectoryWhenDatePlaceHolderExists() + { + var roller = new PathRoller(Path.Combine("Logs", "{Date}-log.txt"), RollingInterval.Day); + AssertEqualAbsolute("Logs", roller.LogFileDirectory); + } + [Fact] public void TheLogFileIsNotRequiredToIncludeAnExtension() { @@ -45,6 +94,15 @@ public void TheLogFileIsNotRequiredToIncludeAnExtension() AssertEqualAbsolute(Path.Combine("Logs", "log-20130714"), path); } + [Fact] + public void TheLogFileIsNotRequiredToIncludeAnExtensionWhenDatePlaceHolderExists() + { + var roller = new PathRoller(Path.Combine("Logs", "{Date}-log"), RollingInterval.Day); + var now = new DateTime(2013, 7, 14, 3, 24, 9, 980); + roller.GetLogFilePath(now, null, out var path); + AssertEqualAbsolute(Path.Combine("Logs", "20130714-log"), path); + } + [Fact] public void TheLogFileIsNotRequiredToIncludeADirectory() { @@ -54,13 +112,24 @@ public void TheLogFileIsNotRequiredToIncludeADirectory() AssertEqualAbsolute("log-20130714", path); } + [Fact] + public void TheLogFileIsNotRequiredToIncludeADirectoryWhenDatePlaceHolderExists() + { + var roller = new PathRoller("{Date}-log", RollingInterval.Day); + var now = new DateTime(2013, 7, 14, 3, 24, 9, 980); + roller.GetLogFilePath(now, null, out var path); + AssertEqualAbsolute("20130714-log", path); + } + [Fact] public void MatchingExcludesSimilarButNonMatchingFiles() { var roller = new PathRoller("log-.txt", RollingInterval.Day); const string similar1 = "log-0.txt"; const string similar2 = "log-hello.txt"; - var matched = roller.SelectMatches(new[] { similar1, similar2 }); + const string similar3 = "0-log.txt"; + const string similar4 = "hello-log.txt"; + var matched = roller.SelectMatches(new[] { similar1, similar2, similar3, similar4 }); Assert.Empty(matched); } @@ -71,9 +140,17 @@ public void TheDirectorSearchPatternUsesWildcardInPlaceOfDate() Assert.Equal("log-*.txt", roller.DirectorySearchPattern); } + [Fact] + public void TheDirectorSearchPatternUsesWildcardInPlaceOfDateWhenDatePlaceHolderExists() + { + var roller = new PathRoller(Path.Combine("Logs", "{Date}-log.txt"), RollingInterval.Day); + Assert.Equal("*-log.txt", roller.DirectorySearchPattern); + } + [Theory] [InlineData("log-.txt", "log-20131210.txt", "log-20131210_031.txt", RollingInterval.Day)] [InlineData("log-.txt", "log-2013121013.txt", "log-2013121013_031.txt", RollingInterval.Hour)] + [InlineData("{Date}-log.txt", "2013121013-log.txt", "2013121013_031-log.txt", RollingInterval.Hour)] public void MatchingSelectsFiles(string template, string zeroth, string thirtyFirst, RollingInterval interval) { var roller = new PathRoller(template, interval); @@ -86,6 +163,7 @@ public void MatchingSelectsFiles(string template, string zeroth, string thirtyFi [Theory] [InlineData("log-.txt", "log-20150101.txt", "log-20141231.txt", RollingInterval.Day)] [InlineData("log-.txt", "log-2015010110.txt", "log-2015010109.txt", RollingInterval.Hour)] + [InlineData("{Date}-log.txt", "2015010110-log.txt", "2015010109-log.txt", RollingInterval.Hour)] public void MatchingParsesSubstitutions(string template, string newer, string older, RollingInterval interval) { var roller = new PathRoller(template, interval); From 939d607d676d3603724284c6208a7155ddc25d94 Mon Sep 17 00:00:00 2001 From: Ramaz Date: Tue, 11 Mar 2025 01:53:12 +0400 Subject: [PATCH 2/2] Added Test Case in RollingFileSinkTests.cs --- .../RollingFileSinkTests.cs | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/test/Serilog.Sinks.File.Tests/RollingFileSinkTests.cs b/test/Serilog.Sinks.File.Tests/RollingFileSinkTests.cs index 830af11..10e15c0 100644 --- a/test/Serilog.Sinks.File.Tests/RollingFileSinkTests.cs +++ b/test/Serilog.Sinks.File.Tests/RollingFileSinkTests.cs @@ -29,6 +29,23 @@ public void LogEventsAreEmittedToTheFileNamedAccordingToTheEventTimestamp() TestRollingEventSequence(Some.InformationEvent()); } + [Fact] + public void ShouldCreateFileWithDatePlaceHolder() + { + var config = new LoggerConfiguration() + .WriteTo.File("{Date}-log.txt", rollingInterval: RollingInterval.Day); + var log = config.CreateLogger(); + + var now = new DateTime(2013, 7, 14, 3, 24, 9, 980); + + Clock.SetTestDateTimeNow(now); + log.Write(Some.InformationEvent()); + + var path = Path.Combine(Directory.GetCurrentDirectory(), "20130714-log.txt"); + + Assert.True(System.IO.File.Exists(path)); + } + [Fact] public void EventsAreWrittenWhenSharingIsEnabled() { @@ -172,7 +189,7 @@ public void WhenFirstOpeningFailedWithLockRetryDelayedUntilNextCheckpoint() e3 = Some.InformationEvent(e1.Timestamp.AddMinutes(5)), e4 = Some.InformationEvent(e1.Timestamp.AddMinutes(31)); LogEvent[] logEvents = new[] { e1, e2, e3, e4 }; - + foreach (var logEvent in logEvents) { Clock.SetTestDateTimeNow(logEvent.Timestamp.DateTime); @@ -210,7 +227,7 @@ public void WhenFirstOpeningFailedWithLockRetryDelayed30Minutes() e3 = Some.InformationEvent(e1.Timestamp.AddMinutes(5)), e4 = Some.InformationEvent(e1.Timestamp.AddMinutes(31)); LogEvent[] logEvents = new[] { e1, e2, e3, e4 }; - + SelfLog.Enable(_testOutputHelper.WriteLine); foreach (var logEvent in logEvents) { @@ -247,7 +264,7 @@ public void WhenFirstOpeningFailedWithoutLockRetryDelayed30Minutes() e3 = Some.InformationEvent(e1.Timestamp.AddMinutes(5)), e4 = Some.InformationEvent(e1.Timestamp.AddMinutes(31)); LogEvent[] logEvents = new[] { e1, e2, e3, e4 }; - + SelfLog.Enable(_testOutputHelper.WriteLine); foreach (var logEvent in logEvents) {