I am having an issue with log4net regarding the use of the rolling appender. When we have mutiple threads we create an RollingAppender to log messages to the default log file as well as a job specific log file. We set a PropertyFilter in RollingAppender with a guid as the key for the thread being used. We also set the ThreadContext.Properties[key] to the same key.
We create the Appender when we start the job, then remove the Appender in the finally block of a try / catch / finally code.
The issue is that randomly, the logger is creating sub-folders that contain the AppName.log as well as the first part of the DatePattern filter in the folder path. For example if the DatePattern is yyyy\MM\'Today'\'{fileName}.log'" and the AppName is SampleApp.exe, then we randomly end up with a folder name as follows:
SampleApp.log2022\11\Today\AppenderName.log.
When it does this, it creates several folders deep using this same naming convention. So we end up with a folder structure like so:
SampleApp.log2022\11\Today\SampleApp.log2022\11\Today\SampleApp.log2022\11\Today\SampleApp.log2022\11\Today\SampleApp.log2022\11\Today\AppenderName.log
Clearly this is a problem.
Here is the Appender code
public void CreateRollingFileAppender
(
string appenderName,
string fileName,
PropertyFilter filter = null,
string logPath = ""
)
{
if (logPath == "") logPath = _logConfiguration.LogPath;
if (Root.GetAppender(appenderName) != null) return;
var patternLayout = new PatternLayout(_logConfiguration.Appender.PatternLayout);
var denyAllFilter = new DenyAllFilter();
var appender = new RollingFileAppender
{
Name = appenderName,
File = logPath,
StaticLogFileName = false,
Layout = patternLayout,
ImmediateFlush = true,
AppendToFile = true,
MaxSizeRollBackups = 5,
MaximumFileSize = _logConfiguration.Appender.MaxSize,
RollingStyle = RollingFileAppender.RollingMode.Date,
DatePattern = $@"yyyy\\MM\\'Today'\\'{fileName}.log'",
LockingModel = new FileAppender.MinimalLock()
};
if (filter != null)
{
appender.AddFilter(filter);
appender.AddFilter(denyAllFilter);
}
appender.ActivateOptions();
BasicConfigurator.Configure(Repository, appender);
}
and here is the code to produce the filter we are using:
public PropertyFilter CreateThreadFilter()
{
var guid = Guid.NewGuid().ToString();
ThreadContext.Properties[Key] = guid;
return new PropertyFilter
{
Key = Key,
StringToMatch = guid,
AcceptOnMatch = true
};
}
Here is the remove appender code:
public void RemoveAppender(string appenderName)
{
var appender = Root.GetAppender(appenderName);
if (appender != null)
{
appender.Close();
Root.RemoveAppender(appenderName);
}
}
and here is an example of calling this code:
CreateRollingFileAppender("Test", "Test", CreateThreadFilter())
try
{
...
logger.Info(message);
}
catch (Exception e)
{
logger.Error(e)
}
finally
{
RemoveAppender("Test")
}
We define Repository and Root as follows:
var Repository = LogManager.CreateRepository(Assembly.GetEntryAssembly(), typeof(Hierarchy));
var Root is ((Hierarchy)Repository).Root
Please advise.
Thanks
So the issue here was not code related, but configuration related. The previous developer used a configuration file to store the properties used to create any files. However, he left the File property empty in the config file. So when first launched, it had no path property to create the file from.