diff --git a/src/changelog/3.2.0/.release.xml b/src/changelog/3.2.0/.release.xml
index aace3b6d..b1a9979b 100644
--- a/src/changelog/3.2.0/.release.xml
+++ b/src/changelog/3.2.0/.release.xml
@@ -2,5 +2,5 @@
diff --git a/src/changelog/3.2.0/250-broken-rolling-logfiles.xml b/src/changelog/3.2.0/250-broken-rolling-logfiles.xml
new file mode 100644
index 00000000..249cfd84
--- /dev/null
+++ b/src/changelog/3.2.0/250-broken-rolling-logfiles.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+ The rolling of logfiles was fixed (reported by @maedula, fixed by @gdziadkiewicz)
+
+
\ No newline at end of file
diff --git a/src/log4net.Tests/Appender/RemoteSyslogAppenderTest.cs b/src/log4net.Tests/Appender/RemoteSyslogAppenderTest.cs
index bc5ea526..2a6c94fb 100644
--- a/src/log4net.Tests/Appender/RemoteSyslogAppenderTest.cs
+++ b/src/log4net.Tests/Appender/RemoteSyslogAppenderTest.cs
@@ -18,6 +18,7 @@
#endregion
using System.Text;
+using System.Threading;
using log4net.Appender;
using log4net.Appender.Internal;
using log4net.Core;
@@ -64,11 +65,17 @@ public void RemoteSyslogTest()
Domain = "TestDomain",
});
appender.DoAppend(loggingEvent);
+ for (int i = 0; i < 20; i++)
+ {
+ if (appender.Mock.Sent.Count == 0)
+ {
+ Thread.Sleep(10);
+ }
+ }
appender.Close();
Assert.That(appender.Mock.ConnectedTo, Is.EqualTo((0, ipAddress, 514)));
Assert.That(appender.Mock.Sent, Has.Count.EqualTo(1));
Assert.That(appender.Mock.WasDisposed, Is.True);
- Assert.That(appender.Mock.Sent, Has.Count.EqualTo(1));
const string expectedData = @"<14>TestDomain: INFO - Test message";
Assert.That(Encoding.ASCII.GetString(appender.Mock.Sent[0].Datagram), Is.EqualTo(expectedData));
}
diff --git a/src/log4net.Tests/Integration/Log4NetIntegrationTests.cs b/src/log4net.Tests/Integration/Log4NetIntegrationTests.cs
index bf29b381..9725c8eb 100644
--- a/src/log4net.Tests/Integration/Log4NetIntegrationTests.cs
+++ b/src/log4net.Tests/Integration/Log4NetIntegrationTests.cs
@@ -1,9 +1,29 @@
+#region Apache License
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to you under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#endregion
+
using System;
-using System.IO;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
-using log4net;
+using log4net.Appender;
using log4net.Config;
+using log4net.Repository;
using NUnit.Framework;
namespace log4net.Tests.Integration
@@ -13,8 +33,9 @@ namespace log4net.Tests.Integration
///
/// Expectations for test log files and directories:
///
- /// - Test log files may be placed directly in the test directory with names starting with TestLogFilePrefix, or inside directories whose names start with TestLogDirectoryPrefix and may have arbitrary file names.
- /// - Each test run starts with a clean environment: any files or directories from previous runs matching these prefixes are deleted in SetUp.
+ /// - Test log files may be placed directly in the test directory with names starting with ,
+ /// or inside directories whose names start with and may have arbitrary file names.
+ /// - Each test run starts with a clean environment: any files or directories from previous runs matching these prefixes are deleted in .
/// - Tests assert the existence, count, and content of log files and directories as part of their validation.
///
///
@@ -44,16 +65,16 @@ public void SetUp()
[Test]
public void Log4Net_WritesLogFile_AndContentIsCorrect()
{
- var (log, repo) = ArrangeLogger("log4net.integration.basic.config");
- log.Info("Hello integration test");
- log.Error("This is an error");
- repo.Shutdown();
+ (ILog log, ILoggerRepository repo) = ArrangeLogger("log4net.integration.basic.config");
+ log.Info("Hello integration test");
+ log.Error("This is an error");
+ repo.Shutdown();
- // Assert: log file exists and contains expected content
- string[] logLines = File.ReadAllLines("integrationTestLogFile_integration.log");
- Assert.That(logLines.Length, Is.EqualTo(2));
- Assert.That(logLines[0], Does.Contain("Hello integration test"));
- Assert.That(logLines[1], Does.Contain("This is an error"));
+ // Assert: log file exists and contains expected content
+ string[] logLines = File.ReadAllLines("integrationTestLogFile_integration.log");
+ Assert.That(logLines, Has.Length.EqualTo(2));
+ Assert.That(logLines[0], Does.Contain("Hello integration test"));
+ Assert.That(logLines[1], Does.Contain("This is an error"));
}
///
@@ -67,21 +88,21 @@ public void Log4Net_WritesLogFile_AndContentIsCorrect()
[Test]
public void Log4Net_WritesLogFile_AndContentIsCorrectAfterRestart()
{
- for (int i = 0; i < 10; i++)
- {
- var (log, repo) = ArrangeLogger("log4net.integration.basic.config");
- log.Info("Hello integration test");
- log.Error("This is an error");
- repo.Shutdown();
- }
- // Assert: log file exists and contains expected content
- string[] logLines = File.ReadAllLines("integrationTestLogFile_integration.log");
- Assert.That(logLines.Length, Is.EqualTo(20));
- for (int i = 0; i < 10; i++)
- {
- Assert.That(logLines[i * 2], Does.Contain("Hello integration test"));
- Assert.That(logLines[i * 2 + 1], Does.Contain("This is an error"));
- }
+ for (int i = 0; i < 10; i++)
+ {
+ (ILog log, ILoggerRepository repo) = ArrangeLogger("log4net.integration.basic.config");
+ log.Info("Hello integration test");
+ log.Error("This is an error");
+ repo.Shutdown();
+ }
+ // Assert: log file exists and contains expected content
+ string[] logLines = File.ReadAllLines("integrationTestLogFile_integration.log");
+ Assert.That(logLines, Has.Length.EqualTo(20));
+ for (int i = 0; i < 10; i++)
+ {
+ Assert.That(logLines[i * 2], Does.Contain("Hello integration test"));
+ Assert.That(logLines[i * 2 + 1], Does.Contain("This is an error"));
+ }
}
///
@@ -96,18 +117,18 @@ public void Log4Net_WritesLogFile_AndContentIsCorrectAfterRestart()
[Test]
public void Log4Net_WritesLogFile_WithRollAndNoAppend_AndContentIsCorrectAfterRestart()
{
- for (int i = 0; i < 20; i++)
+ for (int i = 0; i < 20; i++)
+ {
+ (ILog log, ILoggerRepository repo) = ArrangeLogger("log4net.roll.config");
+ for (int j = 0; j < 10; ++j)
{
- var (log, repo) = ArrangeLogger("log4net.roll.config");
- for (int j = 0; j < 10; ++j)
- {
- log.Info($"Hello, log4net! {i} {j}");
- }
- repo.Shutdown();
+ log.Info($"Hello, log4net! {i} {j}");
}
- // Assert: log file exists and contains expected content
- string[] logFiles = Directory.GetFiles("integrationTestLogDir_roll");
- Assert.That(logFiles.Length, Is.EqualTo(12 + 1));
+ repo.Shutdown();
+ }
+ // Assert: log file exists and contains expected content
+ string[] logFiles = Directory.GetFiles("integrationTestLogDir_roll");
+ Assert.That(logFiles, Has.Length.EqualTo(12 + 1));
}
///
@@ -122,21 +143,19 @@ public void Log4Net_WritesLogFile_WithRollAndNoAppend_AndContentIsCorrectAfterRe
[Test]
public void Log4Net_WritesLogFile_WithMaxSizeRoll_Config_Works()
{
- string logDir = Path.Combine(TestContext.CurrentContext.TestDirectory, "integrationTestLogDir_maxsizeroll");
- if (Directory.Exists(logDir)) Directory.Delete(logDir, true);
- Directory.CreateDirectory(logDir);
- var (log, repo) = ArrangeLogger("log4net.maxsizeroll.config");
- for (int i = 0; i < 1000; ++i)
- {
- log.Info($"Log entry {i}");
- }
- repo.Shutdown();
- // Assert: rolled files exist
- string[] logFiles = Directory.GetFiles(logDir, "*.log");
- Assert.That(logFiles.Length, Is.EqualTo(4)); // 1 current + 3 backups
- // Optionally, check that each file is <= 10KB
- foreach (var file in logFiles)
- Assert.That(new FileInfo(file).Length, Is.LessThanOrEqualTo(10 * 1024 + 100));
+ DirectoryInfo logDir = CreateLogDirectory("integrationTestLogDir_maxsizeroll");
+ (ILog log, ILoggerRepository repo) = ArrangeLogger("log4net.maxsizeroll.config");
+ for (int i = 0; i < 1000; ++i)
+ {
+ log.Info($"Log entry {i}");
+ }
+ repo.Shutdown();
+ // Assert: rolled files exist
+ FileInfo[] logFiles = logDir.GetFiles("*.log");
+ Assert.That(logFiles, Has.Length.EqualTo(4)); // 1 current + 3 backups
+ // Check that each file is <= 10KB
+ foreach (FileInfo file in logFiles)
+ Assert.That(file.Length, Is.LessThanOrEqualTo(10 * 1024 + 100));
}
///
@@ -152,32 +171,35 @@ public void Log4Net_WritesLogFile_WithMaxSizeRoll_Config_Works()
[Test]
public void Log4Net_WritesLogFile_WithDateAndSizeRoll_Config_Works()
{
- string logDir = Path.Combine(TestContext.CurrentContext.TestDirectory, "integrationTestLogDir_maxsizerolldate");
- if (Directory.Exists(logDir)) Directory.Delete(logDir, true);
- Directory.CreateDirectory(logDir);
- var (log, repo) = ArrangeLogger("log4net.maxsizeroll_date.config");
- // Write enough lines to trigger rolling by size and date
- for (int i = 1; i < 10000; ++i)
- {
- log.Debug($"DateRoll entry {i}");
- if (i % 5000 == 0) System.Threading.Thread.Sleep(TimeSpan.FromMinutes(1)); // allow time for date to change if needed
- }
- repo.Shutdown();
- // Assert: rolled files exist (date+size pattern)
- string[] logFiles = Directory.GetFiles(logDir, "*.log");
- Assert.That(logFiles.Length, Is.EqualTo(8));
- // Group files by date part in the filename (yyyy-MM-dd-mm)
- var dateGroups = logFiles
- .Select(f => Path.GetFileNameWithoutExtension(f))
- .Select(name => name.Split('.').First())
- .GroupBy(date => date)
- .ToDictionary(g => g.Key, g => g.Count());
- // Assert that at least one group exists and print group counts
- Assert.That(dateGroups.Count, Is.EqualTo(2));
- foreach (var group in dateGroups)
+ DirectoryInfo logDir = CreateLogDirectory("integrationTestLogDir_maxsizerolldate");
+ (ILog log, ILoggerRepository repo) = ArrangeLogger("log4net.maxsizeroll_date.config");
+ MockDateTime mockDateTime = new();
+ repo.GetAppenders().OfType().First().DateTimeStrategy = mockDateTime;
+ // Write enough lines to trigger rolling by size and date
+ for (int i = 1; i < 10000; ++i)
+ {
+ log.Debug($"DateRoll entry {i}");
+ if (i % 5000 == 0)
{
- TestContext.Out.WriteLine($"Date group: {group.Key}, file count: {group.Value}");
+ mockDateTime.Offset = TimeSpan.FromMinutes(1); // allow time for date to change if needed
}
+ }
+ repo.Shutdown();
+ // Assert: rolled files exist (date+size pattern)
+ FileInfo[] logFiles = logDir.GetFiles("*.log");
+ Assert.That(logFiles, Has.Length.EqualTo(8));
+ // Group files by date part in the filename (yyyy-MM-dd-mm)
+ Dictionary dateGroups = logFiles
+ .Select(f => Path.GetFileNameWithoutExtension(f.Name))
+ .Select(name => name.Split('.').First())
+ .GroupBy(date => date)
+ .ToDictionary(g => g.Key, g => g.Count());
+ // Assert that at least one group exists and print group counts
+ Assert.That(dateGroups, Has.Count.EqualTo(2));
+ foreach (KeyValuePair group in dateGroups)
+ {
+ TestContext.Out.WriteLine($"Date group: {group.Key}, file count: {group.Value}");
+ }
}
///
@@ -192,20 +214,28 @@ public void Log4Net_WritesLogFile_WithDateAndSizeRoll_Config_Works()
[Test]
public void Log4Net_ConfigWithoutFileName_CreatesOneFile()
{
- string logDir = Path.Combine(TestContext.CurrentContext.TestDirectory, "integrationTestLogDir_no_file_name");
- if (Directory.Exists(logDir)) Directory.Delete(logDir, true);
- Directory.CreateDirectory(logDir);
- var (log, repo) = ArrangeLogger("log4net.no_file_name.config");
- log.Info("Test entry with no file name");
- repo.Shutdown();
- // Assert: exactly one log file was created in the directory
- var files = Directory.GetFiles(logDir, "*", SearchOption.AllDirectories);
- Assert.That(files.Length, Is.EqualTo(1), "Should create exactly one log file");
- var fileName = Path.GetFileName(files[0]);
- TestContext.Out.WriteLine($"Created file: {fileName}");
- // Assert the file name matches the date pattern yyyy-MM-dd.log
- string todayPattern = DateTime.Now.ToString("yyyy-MM-dd") + ".log";
- Assert.That(fileName, Is.EqualTo(todayPattern), $"File name should match pattern: {todayPattern}");
+ DirectoryInfo logDir = CreateLogDirectory("integrationTestLogDir_no_file_name");
+ (ILog log, ILoggerRepository repo) = ArrangeLogger("log4net.no_file_name.config");
+ log.Info("Test entry with no file name");
+ repo.Shutdown();
+ // Assert: exactly one log file was created in the directory
+ FileInfo[] files = logDir.GetFiles("*", SearchOption.AllDirectories);
+ Assert.That(files, Has.Length.EqualTo(1), "Should create exactly one log file");
+ TestContext.Out.WriteLine($"Created file: {files[0].Name}");
+ // Assert the file name matches the date pattern yyyy-MM-dd.log
+ string todayPattern = DateTime.Now.ToString("yyyy-MM-dd") + ".log";
+ Assert.That(files[0].Name, Is.EqualTo(todayPattern), $"File name should match pattern: {todayPattern}");
+ }
+
+ private static DirectoryInfo CreateLogDirectory(string name)
+ {
+ DirectoryInfo logDir = new(Path.Combine(TestContext.CurrentContext.TestDirectory, name));
+ if (logDir.Exists)
+ {
+ logDir.Delete(true);
+ }
+ logDir.Create();
+ return logDir;
}
private const string TestLogFilePrefix = "integrationTestLogFile";
@@ -213,32 +243,32 @@ public void Log4Net_ConfigWithoutFileName_CreatesOneFile()
private static void RemoveDirsFromPreviousRuns()
{
- List directoriesFromPreviousRuns =
- Directory.EnumerateDirectories(TestContext.CurrentContext.TestDirectory)
- .Where(dirPath => Path.GetFileName(dirPath).StartsWith(TestLogDirectoryPrefix, StringComparison.InvariantCultureIgnoreCase))
- .ToList();
- directoriesFromPreviousRuns.ForEach(d => Directory.Delete(d, true));
+ List directoriesFromPreviousRuns = new DirectoryInfo(TestContext.CurrentContext.TestDirectory)
+ .EnumerateDirectories()
+ .Where(directory => directory.Name.StartsWith(TestLogDirectoryPrefix, StringComparison.InvariantCultureIgnoreCase))
+ .ToList();
+ directoriesFromPreviousRuns.ForEach(d => d.Delete(true));
}
private static void RemoveFilesFromPreviousRuns()
{
- List filesFromPreviousRuns =
- Directory.EnumerateFiles(TestContext.CurrentContext.TestDirectory)
- .Where(filePath => Path.GetFileName(filePath).StartsWith(TestLogFilePrefix, StringComparison.InvariantCultureIgnoreCase))
- .ToList();
- filesFromPreviousRuns.ForEach(File.Delete);
+ List filesFromPreviousRuns = new DirectoryInfo(TestContext.CurrentContext.TestDirectory)
+ .EnumerateFiles()
+ .Where(filePath => filePath.Name.StartsWith(TestLogFilePrefix, StringComparison.InvariantCultureIgnoreCase))
+ .ToList();
+ filesFromPreviousRuns.ForEach(file => file.Delete());
}
private static string GetConfigPath(string configFile)
=> Path.Combine(TestContext.CurrentContext.TestDirectory, "Integration", configFile);
- private static (ILog log, log4net.Repository.ILoggerRepository repo) ArrangeLogger(string configFile)
+ private static (ILog log, ILoggerRepository repo) ArrangeLogger(string configFile)
{
- string configPath = GetConfigPath(configFile);
- var repo = LogManager.CreateRepository(Guid.NewGuid().ToString());
- XmlConfigurator.Configure(repo, new FileInfo(configPath));
- var log = LogManager.GetLogger(repo.Name, "IntegrationTestLogger");
- return (log, repo);
+ string configPath = GetConfigPath(configFile);
+ ILoggerRepository repo = LogManager.CreateRepository(Guid.NewGuid().ToString());
+ XmlConfigurator.Configure(repo, new FileInfo(configPath));
+ ILog log = LogManager.GetLogger(repo.Name, "IntegrationTestLogger");
+ return (log, repo);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/log4net.Tests/Integration/MockDateTime.cs b/src/log4net.Tests/Integration/MockDateTime.cs
new file mode 100644
index 00000000..efa67d7a
--- /dev/null
+++ b/src/log4net.Tests/Integration/MockDateTime.cs
@@ -0,0 +1,37 @@
+#region Apache License
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to you under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#endregion
+
+using System;
+using log4net.Appender;
+
+namespace log4net.Tests.Integration;
+
+///
+/// Mock implementation of
+///
+internal sealed class MockDateTime : RollingFileAppender.IDateTime
+{
+ ///
+ /// Offset to apply to the current time.
+ ///
+ internal TimeSpan Offset { get; set; }
+
+ ///
+ public DateTime Now => DateTime.Now + Offset;
+}
\ No newline at end of file
diff --git a/src/log4net.Tests/Integration/log4net.no_file_name.config b/src/log4net.Tests/Integration/log4net.no_file_name.config
index d0fc0786..79ba9304 100644
--- a/src/log4net.Tests/Integration/log4net.no_file_name.config
+++ b/src/log4net.Tests/Integration/log4net.no_file_name.config
@@ -1,23 +1,23 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
\ No newline at end of file
diff --git a/src/log4net.Tests/Integration/log4net.roll.config b/src/log4net.Tests/Integration/log4net.roll.config
index 15b6735d..06b4c8ea 100644
--- a/src/log4net.Tests/Integration/log4net.roll.config
+++ b/src/log4net.Tests/Integration/log4net.roll.config
@@ -1,6 +1,6 @@
-
+
-
+
@@ -23,9 +23,9 @@
-
-
-
-
-
+
+
+
+
+
\ No newline at end of file
diff --git a/src/log4net.Tests/NdcTest.cs b/src/log4net.Tests/NdcTest.cs
index ee5b14d3..1b747e3b 100644
--- a/src/log4net.Tests/NdcTest.cs
+++ b/src/log4net.Tests/NdcTest.cs
@@ -17,20 +17,6 @@
//
#endregion
-using System;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using log4net.Config;
-using log4net.Core;
-using log4net.Layout;
-using log4net.Layout.Pattern;
-using log4net.Repository;
-using log4net.Tests.Appender;
-using log4net.Util;
using NUnit.Framework;
namespace log4net.Tests;
diff --git a/src/log4net/Appender/RollingFileAppender.cs b/src/log4net/Appender/RollingFileAppender.cs
index 55105c9f..5e983df3 100644
--- a/src/log4net/Appender/RollingFileAppender.cs
+++ b/src/log4net/Appender/RollingFileAppender.cs
@@ -790,7 +790,7 @@ private void InitializeFromOneFile(string baseFile, string curFileName)
curFileName = curFileName.ToLowerInvariant();
baseFile = baseFile.ToLowerInvariant();
string dir = string.IsNullOrEmpty(baseFile) ? "" : Path.GetDirectoryName(baseFile);
- var baseFileWithoutExtension = Path.Combine(dir ?? "", Path.GetFileNameWithoutExtension(baseFile));
+ string baseFileWithoutExtension = Path.Combine(dir ?? "", Path.GetFileNameWithoutExtension(baseFile));
if (curFileName.StartsWith(baseFileWithoutExtension) == false)
{
@@ -812,7 +812,7 @@ private void InitializeFromOneFile(string baseFile, string curFileName)
string suffix = PreserveLogFileNameExtension
? Path.GetExtension(baseFile).ToLowerInvariant()
: "";
- var curFileNameWithoutDir = Path.GetFileName(curFileName);
+ 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");
diff --git a/src/log4net/Util/Log4NetAssert.cs b/src/log4net/Util/Log4NetAssert.cs
index dda240d4..28ea8894 100644
--- a/src/log4net/Util/Log4NetAssert.cs
+++ b/src/log4net/Util/Log4NetAssert.cs
@@ -1,4 +1,4 @@
-#region Apache License
+#region Apache License
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with