Skip to content

Add tests for regression in file rolling and fix bug #257

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 0 additions & 33 deletions src/log4net.Tests/Appender/RollingFileAppenderWithDirTest.cs

This file was deleted.

186 changes: 186 additions & 0 deletions src/log4net.Tests/Integration/Log4NetIntegrationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
using System;
using System.IO;
using System.Linq;
using log4net;
using log4net.Config;
using NUnit.Framework;

namespace log4net.Tests.Integration
{
[TestFixture]
public class Log4NetIntegrationTests
{
[SetUp]
public void SetUp()
{
var filesFromPreviousRuns =
Directory.EnumerateFiles(TestContext.CurrentContext.TestDirectory)
.Where(filePath => Path.GetFileName(filePath).StartsWith("integrationTestLogFile", StringComparison.InvariantCultureIgnoreCase))
.ToList();
filesFromPreviousRuns.ForEach(File.Delete);
var directoriesFromPreviousRuns =
Directory.EnumerateDirectories(TestContext.CurrentContext.TestDirectory)
.Where(dirPath => Path.GetFileName(dirPath).StartsWith("integrationTestLogDir", StringComparison.InvariantCultureIgnoreCase))
.ToList();
directoriesFromPreviousRuns.ForEach(d => Directory.Delete(d, true));
}


[Test]
public void Log4Net_WritesLogFile_AndContentIsCorrect()
{
// Arrange: configure log4net from config file
var config = "log4net.integration.basic.config";
string configPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Integration", config);
var repo = LogManager.CreateRepository(Guid.NewGuid().ToString());
XmlConfigurator.Configure(repo, new FileInfo(configPath));

// Act: log
var log = LogManager.GetLogger(repo.Name, "IntegrationTestLogger");
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"));
}

[Test]
public void Log4Net_WritesLogFile_AndContentIsCorrectAfterRestart()
{
for (int i = 0; i < 10; i++)
{
// Arrange: configure log4net from config file
var config = "log4net.integration.basic.config";
string configPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Integration", config);
var repo = LogManager.CreateRepository(Guid.NewGuid().ToString());
XmlConfigurator.Configure(repo, new FileInfo(configPath));

// Act: log
var log = LogManager.GetLogger(repo.Name, "IntegrationTestLogger");
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"));
}
}

[Test]
public void Log4Net_WritesLogFile_WithRollAndNoAppend_AndContentIsCorrectAfterRestart()
{
for (int i = 0; i < 20; i++)
{
// Arrange: configure log4net from config file
var config = "log4net.roll.config";
string configPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Integration", config);
var repo = LogManager.CreateRepository(Guid.NewGuid().ToString());
XmlConfigurator.Configure(repo, new FileInfo(configPath));

// Act: log
var log = LogManager.GetLogger(repo.Name, "log");
for (int j = 0; j < 10; ++j)
{
log.Info($"Hello, log4net! {i} {j}");
}
repo.Shutdown();
}

// Assert: log file exists and contains expected content
string[] logFiles = Directory.GetFiles("integrationTestLogDir_roll");
Assert.That(logFiles.Length, Is.EqualTo(12+1));
}

[Test]
public void Log4Net_WritesLogFile_WithMaxSizeRoll_Config_Works()
{
var logDir = Path.Combine(TestContext.CurrentContext.TestDirectory, "integrationTestLogDir_maxsizeroll");
if (Directory.Exists(logDir)) Directory.Delete(logDir, true);
Directory.CreateDirectory(logDir);
var config = "log4net.maxsizeroll.config";
string configPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Integration", config);
var repo = LogManager.CreateRepository(Guid.NewGuid().ToString());
log4net.Config.XmlConfigurator.Configure(repo, new FileInfo(configPath));
var log = LogManager.GetLogger(repo.Name, "log");
// Write enough lines to trigger rolling
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));
}

[Test]
public void Log4Net_WritesLogFile_WithDateAndSizeRoll_Config_Works()
{
var logDir = Path.Combine(TestContext.CurrentContext.TestDirectory, "integrationTestLogDir_maxsizerolldate");
if (Directory.Exists(logDir)) Directory.Delete(logDir, true);
Directory.CreateDirectory(logDir);
var config = "log4net.maxsizeroll_date.config";
string configPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Integration", config);
var repo = LogManager.CreateRepository(Guid.NewGuid().ToString());
log4net.Config.XmlConfigurator.Configure(repo, new FileInfo(configPath));
var log = LogManager.GetLogger(repo.Name, "log");
// 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)
{
TestContext.WriteLine($"Date group: {group.Key}, file count: {group.Value}");
}
}

[Test]
public void Log4Net_ConfigWithoutFileName_CreatesOneFile()
{
var logDir = Path.Combine(TestContext.CurrentContext.TestDirectory, "integrationTestLogDir_no_file_name");
if (Directory.Exists(logDir)) Directory.Delete(logDir, true);
Directory.CreateDirectory(logDir);
var config = "log4net.no_file_name.config";
string configPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Integration", config);
var repo = LogManager.CreateRepository(Guid.NewGuid().ToString());
log4net.Config.XmlConfigurator.Configure(repo, new FileInfo(configPath));
var log = LogManager.GetLogger(repo.Name, "log");
log.Info("Test entry with no file name");
repo.Shutdown();
// Check if 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.WriteLine($"Created file: {fileName}");
// Assert the file name matches the date pattern yyyy-MM-dd.log
var todayPattern = DateTime.Now.ToString("yyyy-MM-dd") + ".log";
Assert.That(fileName, Is.EqualTo(todayPattern), $"File name should match pattern: {todayPattern}");
}
}
}
14 changes: 14 additions & 0 deletions src/log4net.Tests/Integration/log4net.integration.basic.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<appender name="FileAppender" type="log4net.Appender.FileAppender,log4net">
<file value="integrationTestLogFile_integration.log" />
<appendToFile value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="FileAppender" />
</root>
</log4net>
25 changes: 25 additions & 0 deletions src/log4net.Tests/Integration/log4net.maxsizeroll.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<log4net>
<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="File" value="integrationTestLogDir_maxsizeroll/.log" />
<param name="AppendToFile" value="true" />
<param name="RollingStyle" value="Composite" />
<param name="DatePattern" value="yyyy-MM-dd" />
<param name="MaximumFileSize" value="10KB" />
<param name="MaxSizeRollBackups" value="3" />
<param name="StaticLogFileName" value="false" />
<param name="CountDirection" value="1" />
<param name="PreserveLogFileNameExtension" value="true"/>
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy/MM/dd HH:mm:ss.fff} [%-5p] %m (%M)%n"/>
</layout>
</appender>

<logger name="log">
<level value="debug" />
<appender-ref ref="LogFileAppender" />
</logger>
</log4net>
</configuration>
25 changes: 25 additions & 0 deletions src/log4net.Tests/Integration/log4net.maxsizeroll_date.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<log4net>
<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="File" value="integrationTestLogDir_maxsizerolldate/.log" />
<param name="AppendToFile" value="true" />
<param name="RollingStyle" value="Composite" />
<param name="DatePattern" value="yyyy-MM-dd-mm" />
<param name="MaximumFileSize" value="10KB" />
<param name="MaxSizeRollBackups" value="3" />
<param name="StaticLogFileName" value="false" />
<param name="CountDirection" value="1" />
<param name="PreserveLogFileNameExtension" value="true"/>
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy/MM/dd HH:mm:ss.fff} [%-5p] %m (%M)%n"/>
</layout>
</appender>

<logger name="log">
<level value="debug" />
<appender-ref ref="LogFileAppender" />
</logger>
</log4net>
</configuration>
23 changes: 23 additions & 0 deletions src/log4net.Tests/Integration/log4net.no_file_name.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<configuration>
<log4net>
<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
<file value="integrationTestLogDir_no_file_name/"/>
<encoding value="utf-8" />
<datePattern value="yyyy-MM-dd'.log'"/>
<staticLogFileName value="false"/>
<appendToFile value="true"/>
<rollingStyle value="Composite"/>
<maxSizeRollBackups value="10"/>
<maximumFileSize value="5MB"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%property{id}] %-5level %logger - %message%newline"/>
</layout>
</appender>

<logger name="log">
<level value="debug" />
<appender-ref ref="LogFileAppender" />
</logger>
</log4net>
</configuration>
31 changes: 31 additions & 0 deletions src/log4net.Tests/Integration/log4net.roll.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<log4net>
<appender name="Log"
type="log4net.Appender.RollingFileAppender">

<file type="log4net.Util.PatternString">
<conversionPattern value="integrationTestLogDir_roll/Logging.log"/>
</file>

<param name="StaticLogFileName" value="true"/>
<param name="AppendToFile" value="false"/>
<rollingStyle value="Size"/>
<maximumFileSize value="1MB"/>
<maxSizeRollBackups value="12"/>
<CountDirection value="1"/>
<preserveLogFileNameExtension value="true"/>
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>

<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern"
value="%date [%3thread] %-5level %logger{1} %-20message %n"/>
</layout>
</appender>

<logger name="log">
<level value="debug" />
<appender-ref ref="Log" />
</logger>
</log4net>
</configuration>
20 changes: 20 additions & 0 deletions src/log4net.Tests/log4net.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,25 @@
<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNetTestSdkPackageVersion)" />
</ItemGroup>
<ItemGroup>
<None Update="Integration\log4net.maxsizeroll.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Integration\log4net.maxsizeroll_date.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Integration\log4net.no_file_name.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Integration\log4net.roll.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Integration\log4net.roll.config.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Integration\log4net.integration.basic.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<Import Project="../MonoForFramework.targets" />
</Project>
4 changes: 1 addition & 3 deletions src/log4net/Appender/RollingFileAppender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,6 @@ protected List<string> GetExistingFiles(string baseFilePath)
using (SecurityContext?.Impersonate(this))
{
string fullPath = Path.GetFullPath(baseFilePath);
string dir = Path.GetDirectoryName(baseFilePath);

directory = Path.GetDirectoryName(fullPath);
if (Directory.Exists(directory))
Expand All @@ -691,8 +690,7 @@ protected List<string> GetExistingFiles(string baseFilePath)
string[] files = Directory.GetFiles(directory, GetWildcardPatternForFile(baseFileName));
result.AddRange(files
.Select(Path.GetFileName)
.Where(curFileName => curFileName.StartsWith(Path.GetFileNameWithoutExtension(baseFileName)))
.Select(file => Path.Combine(dir, file)));
.Where(curFileName => curFileName.StartsWith(Path.GetFileNameWithoutExtension(baseFileName))));
}
}
LogLog.Debug(_declaringType, "Searched for existing files in [" + directory + "]");
Expand Down