diff --git a/src/changelog/3.5.1/334-rollingfileappender-backup-index.xml b/src/changelog/3.5.1/334-rollingfileappender-backup-index.xml
new file mode 100644
index 00000000..76148837
--- /dev/null
+++ b/src/changelog/3.5.1/334-rollingfileappender-backup-index.xml
@@ -0,0 +1,10 @@
+
+
+
+ Count only the current date's backups when `RollingFileAppender`
+ starts. A date pattern without separators (`.yyyyMMdd`) no longer counts as a backup number, and
+ backups of earlier dates no longer raise the count (reported by @Snotface, fixed by @FreeAndNil)
+
diff --git a/src/log4net.Tests/Appender/RollingFileAppenderTest.cs b/src/log4net.Tests/Appender/RollingFileAppenderTest.cs
index f67c8818..8ef01257 100644
--- a/src/log4net.Tests/Appender/RollingFileAppenderTest.cs
+++ b/src/log4net.Tests/Appender/RollingFileAppenderTest.cs
@@ -1664,6 +1664,163 @@ public void TestInitializeRollBackups4()
VerifyInitializeRollBackups(8, maxRollBackups);
}
+ ///
+ /// Tests that a date roll file using a date pattern without "-" (yyyyMMdd)
+ /// is not mistaken for a backup index, whatever the backup limit
+ ///
+ [TestCase(5, 1)]
+ [TestCase(5, -1)]
+ [TestCase(-1, 1)]
+ [TestCase(-1, -1)]
+ public void TestInitializeRollBackupsIgnoresDateWithoutDashes(int maxSizeRollBackups,
+ int countDirection)
+ {
+ const string baseFile = "LogFile.log";
+ List files = ["LogFile.log", "LogFile.log.20261003", "LogFile.log.1", "LogFile.log.2"];
+
+ RollingFileAppender rfa = new()
+ {
+ RollingStyle = RollingFileAppender.RollingMode.Composite,
+ DatePattern = ".yyyyMMdd",
+ CountDirection = countDirection,
+ MaxSizeRollBackups = maxSizeRollBackups,
+ CurrentSizeRollBackups = 0
+ };
+ rfa.InitializeRollBackups(baseFile, files);
+
+ Assert.That(rfa.CurrentSizeRollBackups, Is.EqualTo(2));
+ }
+
+ ///
+ /// Tests that counting up continues from the highest index, even when
+ /// the oldest backups were deleted and the indexes passed the backup limit
+ ///
+ [Test]
+ public void TestInitializeRollBackupsCountsUpPastLimit()
+ {
+ const string baseFile = "LogFile.log";
+ List files = ["LogFile.log", "LogFile.log.5", "LogFile.log.6", "LogFile.log.7"];
+
+ RollingFileAppender rfa = new()
+ {
+ RollingStyle = RollingFileAppender.RollingMode.Composite,
+ CountDirection = 1,
+ MaxSizeRollBackups = 3,
+ CurrentSizeRollBackups = 0
+ };
+ rfa.InitializeRollBackups(baseFile, files);
+
+ Assert.That(rfa.CurrentSizeRollBackups, Is.EqualTo(7));
+ }
+
+ ///
+ /// Tests that backups already rolled to an earlier date are not counted
+ /// as backups of the current date
+ ///
+ [Test]
+ public void TestInitializeRollBackupsIgnoresOtherDatePeriods()
+ {
+ const string baseFile = "LogFile.log";
+ List files =
+ [
+ "LogFile.log",
+ "LogFile.log.20261003",
+ "LogFile.log.20261003.1",
+ "LogFile.log.20261003.2",
+ "LogFile.log.20261003.3",
+ "LogFile.log.1",
+ ];
+
+ RollingFileAppender rfa = new()
+ {
+ RollingStyle = RollingFileAppender.RollingMode.Composite,
+ DatePattern = ".yyyyMMdd",
+ MaxSizeRollBackups = -1,
+ CurrentSizeRollBackups = 0
+ };
+ rfa.InitializeRollBackups(baseFile, files);
+
+ Assert.That(rfa.CurrentSizeRollBackups, Is.EqualTo(1));
+ }
+
+ ///
+ /// Tests that backups of the current date are found when the date pattern
+ /// supplies the extension and the extension is preserved
+ ///
+ [Test]
+ public void TestInitializeRollBackupsDatePatternWithExtension()
+ {
+ const string baseFile = "log4net";
+ List files =
+ [
+ "log4net.2026-10-04.log",
+ "log4net.2026-10-04.1.log",
+ "log4net.2026-10-04.2.log",
+ "log4net.2026-10-03.log",
+ "log4net.2026-10-03.5.log",
+ ];
+
+ RollingFileAppender rfa = new()
+ {
+ RollingStyle = RollingFileAppender.RollingMode.Composite,
+ DatePattern = ".yyyy-MM-dd'.log'",
+ DateTimeStrategy = new Integration.MockDateTime(new DateTime(2026, 10, 4, 9, 0, 0)),
+ StaticLogFileName = false,
+ PreserveLogFileNameExtension = true,
+ MaxSizeRollBackups = -1,
+ CurrentSizeRollBackups = 0
+ };
+ rfa.InitializeRollBackups(baseFile, files);
+
+ Assert.That(rfa.CurrentSizeRollBackups, Is.EqualTo(2));
+ }
+
+ ///
+ /// Tests that the current date's file of a non-static name is not mistaken for a backup index
+ /// when the date pattern has no separators
+ ///
+ [TestCase("log.txt", ".yyyyMMdd", false, "log.txt.20261004", "log.txt.20261004.1")]
+ [TestCase("log", ".yyyyMMdd'.log'", true, "log.20261004.log", "log.20261004.1.log")]
+ public void TestInitializeRollBackupsNotStaticDateWithoutDashes(string baseFile,
+ string datePattern, bool preserveExtension, string datedFile, string backupFile)
+ {
+ RollingFileAppender rfa = new()
+ {
+ RollingStyle = RollingFileAppender.RollingMode.Composite,
+ DatePattern = datePattern,
+ DateTimeStrategy = new Integration.MockDateTime(new DateTime(2026, 10, 4, 9, 0, 0)),
+ StaticLogFileName = false,
+ PreserveLogFileNameExtension = preserveExtension,
+ MaxSizeRollBackups = -1,
+ CurrentSizeRollBackups = 0
+ };
+ rfa.InitializeRollBackups(baseFile, [datedFile, backupFile]);
+
+ Assert.That(rfa.CurrentSizeRollBackups, Is.EqualTo(1));
+ }
+
+ ///
+ /// Tests that with a short date pattern the suffixes of a static file name count as backup
+ /// indexes, as a date like .10 cannot be told apart from backup 10
+ ///
+ [Test]
+ public void TestInitializeRollBackupsShortDatePatternCountsIndexes()
+ {
+ const string baseFile = "log.txt";
+ List files = ["log.txt", .. Enumerable.Range(1, 10).Select(i => $"log.txt.{i}")];
+
+ RollingFileAppender rfa = new()
+ {
+ RollingStyle = RollingFileAppender.RollingMode.Date,
+ DatePattern = ".dd",
+ MaxSizeRollBackups = -1,
+ CurrentSizeRollBackups = 0
+ };
+ rfa.InitializeRollBackups(baseFile, files);
+
+ Assert.That(rfa.CurrentSizeRollBackups, Is.EqualTo(10));
+ }
+
///
/// Ensures that no problems result from creating and then closing the appender
/// when it has not also been initialized with ActivateOptions().
diff --git a/src/log4net/Appender/RollingFileAppender.cs b/src/log4net/Appender/RollingFileAppender.cs
index d6ad43f6..0cbdd144 100644
--- a/src/log4net/Appender/RollingFileAppender.cs
+++ b/src/log4net/Appender/RollingFileAppender.cs
@@ -879,29 +879,29 @@ private void InitializeFromOneFile(string baseFile, string curFileName)
if (_rollDate && !StaticLogFileName)
{
string date = DateTimeStrategy.Now.ToString(DatePattern, DateTimeFormatInfo.InvariantInfo).ToLowerInvariant();
- string prefix = (PreserveLogFileNameExtension
- ? Path.GetFileNameWithoutExtension(baseFile) + date
- : baseFile + date).ToLowerInvariant();
- string suffix = PreserveLogFileNameExtension
- ? Path.GetExtension(baseFile).ToLowerInvariant()
- : "";
+ // the date pattern may supply the extension, so take it from the dated name
+ string dated = Path.GetFileName(CombinePath(baseFile, date));
+ string suffix = PreserveLogFileNameExtension ? Path.GetExtension(dated) : "";
+ string prefix = dated.Substring(0, dated.Length - suffix.Length);
string curFileNameWithoutDir = Path.GetFileName(curFileName);
if (!curFileNameWithoutDir.StartsWith(prefix) || !curFileNameWithoutDir.EndsWith(suffix))
{
LogLog.Debug(_declaringType, $"Ignoring file [{curFileName}] because it is from a different date period");
return;
}
+
+ // the dated file itself has no index, and a date like .yyyyMMdd would parse as one
+ if (curFileNameWithoutDir.Length <= dated.Length)
+ {
+ return;
+ }
}
try
{
// Bump the counter up to the highest count seen so far
- int backup = GetBackupIndex(curFileName);
+ int backup = GetBackupIndex(baseFile, curFileName);
- // caution: we might get a false positive when certain
- // date patterns such as yyyyMMdd are used...those are
- // valid number but aren't the kind of back up index
- // we're looking for
if (backup > CurrentSizeRollBackups)
{
if (0 == MaxSizeRollBackups)
@@ -945,10 +945,7 @@ private void InitializeFromOneFile(string baseFile, string curFileName)
/// Attempts to extract a number from the end of the file name that indicates
/// the number of the times the file has been rolled over.
///
- ///
- /// Certain date pattern extensions like yyyyMMdd will be parsed as valid backup indexes.
- ///
- private int GetBackupIndex(string curFileName)
+ private int GetBackupIndex(string baseFile, string curFileName)
{
int result = -1;
string fileName = curFileName;
@@ -956,11 +953,20 @@ private int GetBackupIndex(string curFileName)
if (PreserveLogFileNameExtension)
{
fileName = Path.GetFileNameWithoutExtension(fileName);
+ baseFile = Path.GetFileNameWithoutExtension(baseFile);
}
int index = fileName.LastIndexOf(".", StringComparison.Ordinal);
if (index > 0)
{
+ // with a static name, log.txt.20261003.1 is a backup of an earlier date
+ if (StaticLogFileName
+ && (!string.Equals(fileName.Substring(0, index), baseFile, StringComparison.Ordinal)
+ || IsDateSuffix(fileName.Substring(index))))
+ {
+ return -1;
+ }
+
// if the "yyyy-MM-dd" component of file.log.yyyy-MM-dd is passed to TryParse
// it will gracefully fail and return backUpIndex will be 0
_ = SystemInfo.TryParse(fileName.Substring(index + 1), out result);
@@ -969,6 +975,16 @@ private int GetBackupIndex(string curFileName)
return result;
}
+ ///
+ /// Determines whether is a date longer than four characters after
+ /// the dot, such as .yyyyMMdd. Shorter dates (.dd writes .10) cannot be
+ /// told from a backup index and keep counting as one.
+ ///
+ private bool IsDateSuffix(string suffix)
+ => _rollDate && DatePattern is not null && suffix.Length > 5
+ && DateTime.TryParseExact(suffix, DatePattern, DateTimeFormatInfo.InvariantInfo,
+ DateTimeStyles.None, out _);
+
///
/// Takes a list of files and a base file name, and looks for 'incremented' versions of the base file.
/// Bumps the max count up to the highest count seen.