如何使用 log4net 每个任务登录到不同的文件?

Posted

技术标签:

【中文标题】如何使用 log4net 每个任务登录到不同的文件?【英文标题】:How to log into a different file per Task with log4net? 【发布时间】:2021-11-25 15:25:56 【问题描述】:

我开发了一个并行运行多个任务的应用程序。为了使应用程序的日志文件更易于阅读,我希望这些任务中的每一个都通过 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\%propertyLogicName.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 文件和一个包含所有应用程序日志的 (null).log 文件。 理想情况下,我希望在 LogicalThreadContext 中没有指定 LogicName 的所有内容都自动记录在主日志文件中。 我的 log4net 配置必须看起来如何才能使其正常工作?

【问题讨论】:

【参考方案1】:

由于每个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 = $"nameAppender",
                File = $"name.log"
            ;

            PatternLayout layout = new PatternLayout
            
                ConversionPattern = "%datehh:mm:ss.fff %level %thread %logger %propertyLogicName - %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 进行日志记录?

Log4net 登录到 Azure 存储帐户

多线程代码执行的 Log4Net C# 日志记录问题

log4net日志系统使用详解

如何让我的 Rails 应用程序的每个独角兽工作者登录到不同的文件?

(转)Log4Net 详解