【问题标题】:Logging to an individual log file for each individual thread为每个单独的线程记录到单独的日志文件
【发布时间】:2011-10-20 19:52:14
【问题描述】:

我有一个服务应用程序,它在启动时读取一个 XML 文件并为 XML 文件中的每个条目启动一个线程。每个线程创建一个工作者类的实例,该实例需要记录器将任何输出记录到线程特定的日志文件中。

在服务 app.config 中,我将 log4net 配置设置设置为使用 XML 附加程序,并将文件指定为 PatternString,如下所示:

<appender name="XmlAppender" type="log4net.Appender.FileAppender">
  <file type="log4net.Util.PatternString" value="D:\Temp\Logs\%property{LogName}.log" />
  <immediateFlush value="true"/>
  <appendToFile value="true" />
  <layout type="log4net.Layout.SimpleLayout" />
</appender>

在创建的每个工作类实例的线程锁定方法中,我使用log4net.LogManager.GetLogger("MyLogger") 方法获取记录器,然后使用ThreadContext.Properties["LogName"] = "Log name prefix" 设置当前线程PatternStrings LogName 属性。

所有文件都已创建,但当记录器被调用时,它只会将所有消息记录到一个看似随机的文件中。

我已经搜索了很长一段时间试图找到解决方案或我做错的一些答案,但我没有运气。

有人知道为什么会这样吗?

【问题讨论】:

    标签: c# multithreading logging log4net


    【解决方案1】:

    我想我已经解决了这个问题。步骤如下:

    • 在每个线程上创建一个单独的ILoggerRepository 对象(在本例中为loggerRepository)。
    • 为日志文件名设置 ThreadContexts 属性。
    • 使用 XmlConfiguratior 配置存储库。
    • 使用 LogManager 为该线程使用命名的 LoggerRepository 获取命名的记录器(在 XML 配置文件中)。

    作为回报,我得到一个新配置的记录器,指向该线程的相应文件。

    XML 配置与最初的配置相同,为了完整起见,此处显示:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <configSections>    
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
      </configSections>
      <log4net>
        <logger name="ProductionLogger">
          <appender-ref ref="XmlAppender"/>      
          <level value="ALL"/>
        </logger>
        <appender name="XmlAppender" type="log4net.Appender.FileAppender">
          <file type="log4net.Util.PatternString" value="D:\Temp\Logs\%property{LogName}.log" />
          <immediateFlush value="true"/>
          <appendToFile value="true" />
          <layout type="log4net.Layout.SimpleLayout" />
        </appender>
      </log4net>
    </configuration>
    

    创建记录器的代码如下。每次运行此代码时,它都会在自己的线程中运行。

    ILoggerRepository loggerRepository = LogManager.CreateRepository(logFileName + "Repository");
    ThreadContext.Properties["LogName"] = logFileName;
    log4net.Config.XmlConfigurator.Configure(loggerRepository);
    ILog logger = LogManager.GetLogger(logFileName + "Repository", "ProductionLogger");
    

    到目前为止,这似乎没有问题。目前我将继续使用此解决方案,但如果我发现其他任何内容,我将更新此帖子。

    【讨论】:

    • 非常感谢!我在线程和登录到不同的文件时遇到了同样的问题。我浏览了很多帖子和解决方案,但只有这一个帮助了我。
    【解决方案2】:

    亚当的回答对我来说效果很好,但我想补充一个。如果您的 logFileName 有可能在您的应用程序中被重用,您需要检查以确保存储库不存在。

    string repoName = String.Format("{0}Repository", logFileName);
    
    // Check for existing repository
    ILoggerRepository[] allRepos = LogManager.GetAllRepositories();
    ILoggerRepository repo = allRepos.Where(x => x.Name == repoName).FirstOrDefault();
    
    // If repository does not exist, create one, set the logfile name, and configure it
    if (repo == null)
    {
        repo = LogManager.CreateRepository(repoName);
        ThreadContext.Properties[KEY_LOG_FILE] = logFileName;
        log4net.Config.XmlConfigurator.Configure(repo);
    }
    
    // Set logger
    ILog logger = LogManager.GetLogger(repoName, logName);
    

    【讨论】:

    • 可以直接调用FirstOrDefault,不用Where,像这样:allRepos.FirstOrDefault(x =&gt; x.Name == repoName);
    【解决方案3】:

    @Adam 的解决方案帮助了我。对于后代,这里有一个简单的代码,它创建了 5 个任务(使用线程池中的线程),其中在每个任务中,每个线程写入一个单独的日志文件(然后休眠 1 秒),五次。

    .NET 框架 4.7.2
    .NET 控制台应用程序

    AssemblyInfo.cs

    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    
    // General Information about an assembly is controlled through the following
    // set of attributes. Change these attribute values to modify the information
    // associated with an assembly.
    [assembly: AssemblyTitle("LogfilePerThread")]
    [assembly: AssemblyDescription("")]
    [assembly: AssemblyConfiguration("")]
    [assembly: AssemblyCompany("Microsoft")]
    [assembly: AssemblyProduct("LogfilePerThread")]
    [assembly: AssemblyCopyright("Copyright © Microsoft 2020")]
    [assembly: AssemblyTrademark("")]
    [assembly: AssemblyCulture("")]
    
    // Setting ComVisible to false makes the types in this assembly not visible
    // to COM components.  If you need to access a type in this assembly from
    // COM, set the ComVisible attribute to true on that type.
    [assembly: ComVisible(false)]
    
    // The following GUID is for the ID of the typelib if this project is exposed to COM
    [assembly: Guid("d3ec8b29-e701-424e-80d4-473e9fbb5572")]
    
    // Version information for an assembly consists of the following four values:
    //
    //      Major Version
    //      Minor Version
    //      Build Number
    //      Revision
    //
    // You can specify all the values or you can default the Build and Revision Numbers
    // by using the '*' as shown below:
    // [assembly: AssemblyVersion("1.0.*")]
    [assembly: AssemblyVersion("1.0.0.0")]
    [assembly: AssemblyFileVersion("1.0.0.0")]
    

    Program.cs

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using log4net;
    using log4net.Repository;
    
    namespace LogfilePerThread
    {
        class Program
        {
            static void Main(string[] args)
            {
                int nWorkers = 5; // 5 workers, 5 log files
                Task[] Workers = new Task[nWorkers];
                for (int i = 0; i < nWorkers; i++)
                {
                    int i1 = i;
                    Workers[i] = new Task(() => Work(i1));
                    Workers[i].Start();
                }
    
                Task.WaitAll(Workers);
                Console.WriteLine("Main executed.");
            }
    
            public static void Work(int workerID)
            {
                // Make sure the directory where logfiles are written exists
                string logfileName = $"Worker{workerID}_TaskLog"; // Logfile extension & path specified in App.config
                ILoggerRepository repository = LogManager.CreateRepository($"{logfileName}Repository");
                ThreadContext.Properties["WorkerLoggerProperty"] = logfileName;
                log4net.Config.XmlConfigurator.Configure(repository);
                ILog log = LogManager.GetLogger($"{logfileName}Repository", "WorkerLogger"); // Use this logger object in thread
    
                // Do work: Log & Sleep
                for (int i = 0; i < 5; i++)
                {
                    log.Info($"Logging from WorkerID={workerID} - Msg {i}");
                    Thread.Sleep(1000);
                }
            }
        }
    }
    

    App.config

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <configSections>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
      </configSections>
    
      <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
      </startup>
    
      <log4net>
    
        <logger name="WorkerLogger">
          <level value="DEBUG" />
          <appender-ref ref="RollingFile" />
        </logger>
    
        <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
          <file type="log4net.Util.PatternString" value="C:\logs\%property{WorkerLoggerProperty}.log" />
          <appendToFile value="true" />
          <immediateFlush value="true"/>
          <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%date [%5level, %thread] %message%newline" />
          </layout>
        </appender>
    
      </log4net>
    
    </configuration>
    

    C:\logs\Worker0_TaskLog.log

    2020-01-14 21:59:46,696 [ INFO, 3] Logging from WorkerID=0 - Msg 0
    2020-01-14 21:59:47,714 [ INFO, 3] Logging from WorkerID=0 - Msg 1
    2020-01-14 21:59:48,714 [ INFO, 3] Logging from WorkerID=0 - Msg 2
    2020-01-14 21:59:49,714 [ INFO, 3] Logging from WorkerID=0 - Msg 3
    2020-01-14 21:59:50,714 [ INFO, 3] Logging from WorkerID=0 - Msg 4
    

    C:\logs\Worker1_TaskLog.log

    2020-01-14 21:59:46,696 [ INFO, 4] Logging from WorkerID=1 - Msg 0
    2020-01-14 21:59:47,714 [ INFO, 4] Logging from WorkerID=1 - Msg 1
    2020-01-14 21:59:48,714 [ INFO, 4] Logging from WorkerID=1 - Msg 2
    2020-01-14 21:59:49,714 [ INFO, 4] Logging from WorkerID=1 - Msg 3
    2020-01-14 21:59:50,714 [ INFO, 4] Logging from WorkerID=1 - Msg 4
    

    C:\logs\Worker2_TaskLog.log

    2020-01-14 21:59:46,697 [ INFO, 5] Logging from WorkerID=2 - Msg 0
    2020-01-14 21:59:47,713 [ INFO, 5] Logging from WorkerID=2 - Msg 1
    2020-01-14 21:59:48,713 [ INFO, 5] Logging from WorkerID=2 - Msg 2
    2020-01-14 21:59:49,713 [ INFO, 5] Logging from WorkerID=2 - Msg 3
    2020-01-14 21:59:50,713 [ INFO, 5] Logging from WorkerID=2 - Msg 4
    

    C:\logs\Worker3_TaskLog.log

    2020-01-14 21:59:46,696 [ INFO, 6] Logging from WorkerID=3 - Msg 0
    2020-01-14 21:59:47,714 [ INFO, 6] Logging from WorkerID=3 - Msg 1
    2020-01-14 21:59:48,714 [ INFO, 6] Logging from WorkerID=3 - Msg 2
    2020-01-14 21:59:49,714 [ INFO, 6] Logging from WorkerID=3 - Msg 3
    2020-01-14 21:59:50,714 [ INFO, 6] Logging from WorkerID=3 - Msg 4
    

    C:\logs\Worker4_TaskLog.log

    2020-01-14 21:59:47,580 [ INFO, 7] Logging from WorkerID=4 - Msg 0
    2020-01-14 21:59:48,580 [ INFO, 7] Logging from WorkerID=4 - Msg 1
    2020-01-14 21:59:49,580 [ INFO, 7] Logging from WorkerID=4 - Msg 2
    2020-01-14 21:59:50,580 [ INFO, 7] Logging from WorkerID=4 - Msg 3
    2020-01-14 21:59:51,580 [ INFO, 7] Logging from WorkerID=4 - Msg 4
    

    【讨论】:

      猜你喜欢
      • 2012-03-26
      • 1970-01-01
      • 1970-01-01
      • 2013-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-01
      • 2022-01-19
      相关资源
      最近更新 更多