如何使用 log4net 为每个任务登录不同的文件?
How to log into a different file per Task with log4net?
我开发了一个 运行 并行执行多个任务的应用程序。为了使应用程序的日志文件更易于阅读,我希望这些任务中的每一个都通过 log4net 登录到自己的日志文件中。
此外,我还希望将在任务之外记录的所有内容都记录到“主”日志文件中,这样我每个任务都有一个日志文件,还有一个日志文件包含除任务内部记录的内容之外的所有内容。
这是我当前的 log4net 配置 xml:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<appender name="MainRollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<filter type="log4net.Filter.PropertyFilter">
<key value="LogicName" />
<regexToMatch value="^(?!Main$).*$" />
<acceptOnMatch value="false" />
</filter>
<param name="File" value="C:\Temp\Test\Main.log" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="100" />
<maximumFileSize value="10MB" />
<staticLogFileName value="true" />
<datePattern value="yyyyMMdd" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout, log4net">
<conversionPattern value="%date [%thread] %-5level %logger: %message%newline" />
</layout>
</appender>
<appender name="TaskRollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<filter type="log4net.Filter.PropertyFilter">
<key value="LogicName" />
<regexToMatch value="^(?!Main$).*$" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
<file type="log4net.Util.PatternString" value="C:\Temp\Test\%property{LogicName}.log" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="100" />
<maximumFileSize value="10MB" />
<datePattern value="yyyyMMdd" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout, log4net">
<conversionPattern value="%date [%thread] %-5level %logger: %message%newline" />
</layout>
</appender>
<root>
<level value="ALL" />
<appender-ref ref="MainRollingLogFileAppender" />
<appender-ref ref="LogicRollingLogFileAppender" />
</root>
</log4net>
</configuration>
创建单个任务时,我立即运行这行代码为该任务设置当前逻辑名称(logicName
包含当前任务中执行的逻辑名称):
log4net.LogicalThreadContext.Properties["LogicName"] = logicName;
每个任务都是这样开始的:
Task.Run(async () =>
{
await executeLogic(); // The first line in this function sets the logicName in the LogicalThreadContext
}, cancellationToken);
遗憾的是,所有这些所做的只是创建一个 Main.log
文件,该文件在应用程序 运行ning 期间保持为空,以及一个包含所有应用程序日志的 (null).log
文件。
理想情况下,我希望在 LogicalThreadContext 中没有指定 LogicName
的所有内容都自动记录在主日志文件中。
我的 log4net 配置必须如何才能使这项工作正常进行?
由于每个 RollingFileAppender
都有一个文件句柄,因此不可能使用相同的附加程序动态创建新文件。在这种情况下您不能使用配置文件 - 每次创建新的 Thread
.
时都必须以编程方式创建新的附加程序
这是一个如何实现该目标的示例。来自 Main
的所有日志都将发送到 Main.log
,每个线程都有自己的日志文件。
using log4net;
using log4net.Appender;
using log4net.Config;
using log4net.Filter;
using log4net.Layout;
using System.Threading;
using System.Threading.Tasks;
namespace AppenderTest
{
static class Program
{
static ILog log;
static void Main(string[] args)
{
ConfigureAppender("Main");
log = LogManager.GetLogger(typeof(Program));
log.Info("Starting from Main!");
Task task1 = Task.Run(() => NewThread("LogicName1"));
log.Info("Hello from Main!");
Task task2 = Task.Run(() => NewThread("LogicName2"));
log.Info("Hello again from Main!");
Task.WaitAll(task1, task2);
log.Info("Still from Main!");
}
public static void NewThread(string name)
{
ConfigureAppender(name);
for (int i = 0; i < 10; i++)
{
Thread.Sleep(50);
log.Info($"Loop index {i}");
}
}
private static void ConfigureAppender(string name)
{
RollingFileAppender appender = new RollingFileAppender
{
Name = $"{name}Appender",
File = $"{name}.log"
};
PatternLayout layout = new PatternLayout
{
ConversionPattern = "%date{hh:mm:ss.fff} %level %thread %logger %property{LogicName} - %message%newline"
};
layout.ActivateOptions();
appender.Layout = layout;
PropertyFilter filter = new PropertyFilter
{
Key = "LogicName",
StringToMatch = name,
AcceptOnMatch = true
};
filter.ActivateOptions();
appender.AddFilter(filter);
appender.AddFilter(new DenyAllFilter());
appender.ActivateOptions();
LogicalThreadContext.Properties["LogicName"] = name;
BasicConfigurator.Configure(appender);
}
}
}
我开发了一个 运行 并行执行多个任务的应用程序。为了使应用程序的日志文件更易于阅读,我希望这些任务中的每一个都通过 log4net 登录到自己的日志文件中。 此外,我还希望将在任务之外记录的所有内容都记录到“主”日志文件中,这样我每个任务都有一个日志文件,还有一个日志文件包含除任务内部记录的内容之外的所有内容。 这是我当前的 log4net 配置 xml:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<appender name="MainRollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<filter type="log4net.Filter.PropertyFilter">
<key value="LogicName" />
<regexToMatch value="^(?!Main$).*$" />
<acceptOnMatch value="false" />
</filter>
<param name="File" value="C:\Temp\Test\Main.log" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="100" />
<maximumFileSize value="10MB" />
<staticLogFileName value="true" />
<datePattern value="yyyyMMdd" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout, log4net">
<conversionPattern value="%date [%thread] %-5level %logger: %message%newline" />
</layout>
</appender>
<appender name="TaskRollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<filter type="log4net.Filter.PropertyFilter">
<key value="LogicName" />
<regexToMatch value="^(?!Main$).*$" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
<file type="log4net.Util.PatternString" value="C:\Temp\Test\%property{LogicName}.log" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="100" />
<maximumFileSize value="10MB" />
<datePattern value="yyyyMMdd" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout, log4net">
<conversionPattern value="%date [%thread] %-5level %logger: %message%newline" />
</layout>
</appender>
<root>
<level value="ALL" />
<appender-ref ref="MainRollingLogFileAppender" />
<appender-ref ref="LogicRollingLogFileAppender" />
</root>
</log4net>
</configuration>
创建单个任务时,我立即运行这行代码为该任务设置当前逻辑名称(logicName
包含当前任务中执行的逻辑名称):
log4net.LogicalThreadContext.Properties["LogicName"] = logicName;
每个任务都是这样开始的:
Task.Run(async () =>
{
await executeLogic(); // The first line in this function sets the logicName in the LogicalThreadContext
}, cancellationToken);
遗憾的是,所有这些所做的只是创建一个 Main.log
文件,该文件在应用程序 运行ning 期间保持为空,以及一个包含所有应用程序日志的 (null).log
文件。
理想情况下,我希望在 LogicalThreadContext 中没有指定 LogicName
的所有内容都自动记录在主日志文件中。
我的 log4net 配置必须如何才能使这项工作正常进行?
由于每个 RollingFileAppender
都有一个文件句柄,因此不可能使用相同的附加程序动态创建新文件。在这种情况下您不能使用配置文件 - 每次创建新的 Thread
.
这是一个如何实现该目标的示例。来自 Main
的所有日志都将发送到 Main.log
,每个线程都有自己的日志文件。
using log4net;
using log4net.Appender;
using log4net.Config;
using log4net.Filter;
using log4net.Layout;
using System.Threading;
using System.Threading.Tasks;
namespace AppenderTest
{
static class Program
{
static ILog log;
static void Main(string[] args)
{
ConfigureAppender("Main");
log = LogManager.GetLogger(typeof(Program));
log.Info("Starting from Main!");
Task task1 = Task.Run(() => NewThread("LogicName1"));
log.Info("Hello from Main!");
Task task2 = Task.Run(() => NewThread("LogicName2"));
log.Info("Hello again from Main!");
Task.WaitAll(task1, task2);
log.Info("Still from Main!");
}
public static void NewThread(string name)
{
ConfigureAppender(name);
for (int i = 0; i < 10; i++)
{
Thread.Sleep(50);
log.Info($"Loop index {i}");
}
}
private static void ConfigureAppender(string name)
{
RollingFileAppender appender = new RollingFileAppender
{
Name = $"{name}Appender",
File = $"{name}.log"
};
PatternLayout layout = new PatternLayout
{
ConversionPattern = "%date{hh:mm:ss.fff} %level %thread %logger %property{LogicName} - %message%newline"
};
layout.ActivateOptions();
appender.Layout = layout;
PropertyFilter filter = new PropertyFilter
{
Key = "LogicName",
StringToMatch = name,
AcceptOnMatch = true
};
filter.ActivateOptions();
appender.AddFilter(filter);
appender.AddFilter(new DenyAllFilter());
appender.ActivateOptions();
LogicalThreadContext.Properties["LogicName"] = name;
BasicConfigurator.Configure(appender);
}
}
}