How to make NLog thread-safely write to separate file target at runtime?

Viewed 1316

I have an application which runs many threads (~50), every thread acquires a payment with lock to be processed from a database, and then processes it.
However, 50 threads make logs unreadable, mixed up and of enormous filesize.
Now, I want to make NLog write to a separate file for every payment ID, so that all history on payment processment is stored in one file.

NLog configuration file:

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

My code now:

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);
}

What I expect it to be:

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);
}

I have found some solutions which use LogManager.Configuration, but it doesn't seem to be thread-safe.

2 Answers
Related