如何在运行时使 NLog 线程安全地写入单独的文件目标?

Yel*_*yev 2 c# multithreading nlog

我有一个运行多个线程(~50 个)的应用程序,每个线程从数据库中获取要处理的带锁的付款,然后对其进行处理。
但是,50 个线程会使日志不可读、混乱且文件大小庞大。
现在,我想让 NLog 为每个付款 ID 写入一个单独的文件,以便所有付款处理历史记录都存储在一个文件中。

NLog 配置文件:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <targets>
    <target name="file" xsi:type="AsyncWrapper" queueLimit="10000" overflowAction="Discard">
      <target name="f" xsi:type="File" fileName="C:\LogFiles\Immediate\${shortdate}.server.log" layout="${longdate}|${level}|${processid}|${threadid}|${level:upperCase=true}|${callsite:className=false}|${message}" encoding="utf-8"/>
    </target>
  </targets>
  <rules>
    <logger name="*" minlevel="Debug" writeTo="file"/>
  </rules>
</nlog>
Run Code Online (Sandbox Code Playgroud)

我现在的代码:

private static readonly ILogger Logger = LogManager.GetCurrentClassLogger(); // "2017-07-20.log"

while (!_cancellationToken.IsCancellationRequested)
{
    using (var session = _connectionFactory.GetSession())
    {
        AcquirePaymentWithUpdlockReadpast(session, 
            Logger,  // "2017-07-20.log" 
            payment => {
                if (payment == null)
                    return;

                Logger.Debug($"Payment {payment.Id} has been acquired for processment");

                ProcessPayment(session, Logger, payment); // "2017-07-20.log"
            });
    }

    Thread.Sleep(50);
}
Run Code Online (Sandbox Code Playgroud)

我期望它是什么:

private static readonly ILogger GeneralLogger = LogManager.GetCurrentClassLogger(); // "2017-07-20.General.log"

while (!_cancellationToken.IsCancellationRequested)
{
    using (var session = _connectionFactory.GetSession())
    {
        AcquirePaymentWithUpdlockReadpast(session, 
            GeneralLogger, // "2017-07-20.General.log"
            payment => {
                if (payment == null)
                    return;

                var paymentLogger = LogManager.GetCurrentClassLogger();
                paymentLogger.FileName = $"Payment-{payment.Id}.log"; // <-- I want this. 
                paymentLogger.Debug($"Payment has been acquired for processment");

                ProcessPayment(session, paymentLogger, payment); // "2017-07-20.Payment-123.log"
            });
    }

    Thread.Sleep(50);
}
Run Code Online (Sandbox Code Playgroud)

我找到了一些使用 的解决方案LogManager.Configuration,但它似乎不是线程安全的。

Rol*_*sen 5

也许是这样的:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <targets>
    <target name="file" xsi:type="AsyncWrapper" queueLimit="10000" overflowAction="Discard">
      <target name="f" xsi:type="File" fileName="C:\LogFiles\Immediate\${shortdate}.server.log" layout="${longdate}|${level}|${processid}|${threadid}|${level:upperCase=true}|${callsite:className=false}|${message}" encoding="utf-8"/>
    </target>
    <target name="paymentFile" xsi:type="AsyncWrapper" queueLimit="10000" overflowAction="Discard">
      <target name="p" xsi:type="File" fileName="C:\LogFiles\Immediate\Payment-${event-properties:PaymentID:whenEmpty=0}.log" layout="${longdate}|${level}|${processid}|${threadid}|${level:upperCase=true}|${callsite:className=false}|${message}" encoding="utf-8"/>
    </target>
  </targets>
  <rules>
    <logger name="*" minlevel="Debug" writeTo="paymentFile">
         <condition="'${event-properties:PaymentID:whenEmpty=0}'!='0'" action="LogFinal" />
    </logger>
    <logger name="*" minlevel="Debug" writeTo="file"/>
  </rules>
</nlog>
Run Code Online (Sandbox Code Playgroud)

然后你可以做这样的日志记录(可以用 NLog-Fluent 改进):

LogEventInfo theEvent = new LogEventInfo(LogLevel.Debug, $"Payment has been acquired for processment");
theEvent.Properties["PaymentID"] = payment.Id;
Logger.Debug(theEvent);
Run Code Online (Sandbox Code Playgroud)

替代解决方案可能是这样的:

var paymentLogger = LogManager.GetLogger($"Payment-{payment.Id}");
paymentLogger.Debug($"Payment has been acquired for processment")
Run Code Online (Sandbox Code Playgroud)

然后将记录器过滤器更改为:

<logger name="Payment-*" minlevel="Debug" writeTo="paymentFile" />
Run Code Online (Sandbox Code Playgroud)

然后将 paymentFile 文件名更改为:

fileName="C:\LogFiles\Immediate\{logger}.log" 
Run Code Online (Sandbox Code Playgroud)