C# 记录到每个线程的单个日志文件

C# 记录到每个线程的单个日志文件,c#,multithreading,logging,log4net,C#,Multithreading,Logging,Log4net,我有一个服务应用程序,它在启动时读取一个XML文件,并为XML文件中的每个条目启动一个线程。每个线程创建一个worker类的实例,该实例要求记录器将任何输出记录到特定于线程的日志文件中 在services app.config中,我将log4net配置设置为使用XML appender,并将文件指定为PatternString,如下所示: 在为创建的worker类的每个实例使用的线程锁定方法中,我使用log4net.LogManager.GetLogger(“MyLogger”)方法获取记录

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

在services app.config中,我将log4net配置设置为使用XML appender,并将文件指定为PatternString,如下所示:


在为创建的worker类的每个实例使用的线程锁定方法中,我使用
log4net.LogManager.GetLogger(“MyLogger”)
方法获取记录器,然后使用
ThreadContext.Properties[“LogName”]=“Log name prefix”
设置当前线程模式字符串LogName属性

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

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


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

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

  • 在每个线程上创建一个单独的
    ILoggerRepository
    对象(本例中为
    loggerRepository
  • 为日志文件名设置ThreadContexts属性
  • 使用XmlConfiguratior配置存储库
  • 使用LogManager使用该线程的命名LoggerRepository获取命名记录器(在XML配置文件中)
作为回报,我得到一个新配置的记录器,指向该线程的相应文件

XML配置与最初的配置相同,出于完整性考虑,此处显示:


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

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

到目前为止,这似乎没有任何问题。目前我将继续使用这个解决方案,但如果我发现其他问题,我会更新这篇文章。

亚当的答案对我来说非常有效,但我想补充一点。如果有可能在应用程序中重用日志文件名,则需要检查以确保存储库不存在

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);
@他的解决方案帮助了我。对于子孙后代,这里有一个简单的代码,它创建了5个任务(使用线程池中的线程),其中每个任务中的每个线程都会写入一个单独的日志文件(然后休眠1秒),5次

.NET Framework 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")]
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);
            }
        }
    }
}
Program.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")]
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);
            }
        }
    }
}
C:\logs\Worker1\u TaskLog.log

<?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>
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
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
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
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
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
C:\logs\Worker2\u TaskLog.log

<?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>
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
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
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
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
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
C:\logs\Worker3\u TaskLog.log

<?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>
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
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
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
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
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
C:\logs\Worker4\u TaskLog.log

<?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>
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
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
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
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
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

您可以直接调用FirstOrDefault,无需Where,如下所示:
allRepos.FirstOrDefault(x=>x.Name==repoName)非常感谢!我在线程和登录不同的文件时也遇到了同样的问题。我浏览了很多帖子和解决方案,但只有这一篇让我感到惊讶。