ligos ligos - 3 months ago 25
C# Question

Should NLog flush all queued messages in the AsyncTargetWrapper when Flush() is called?

I want to shut down my application and write any pending log messages. So I call

LogManager.Flush()
during my shutdown process. However, I don't see all the messages written out. Instead, if I wait for a few seconds (using
Thread.Sleep()
) I see the messages.

After examining NLog's code on GitHUB, I find the
AsyncTargetWrapper.FlushAsync()
method is only scheduling the lazy writer thread to write all pending messages on the next batch. It is not writing log messages synchronously.

Is this the expected behaviour? I'm expecting
LogManager.Flush()
to be synchronous, ie: to block until all pending messages are written (or the timeout is exceeded).




Code I use on shutdown:

LogManager.Flush(ex => { }, TimeSpan.FromSeconds(15));


And then the code to initialise Nlog (this is a Silverlight app, so I'm not using any config files).

public static void InitialiseNLog(LogLevel forLevel)
{
var config = new LoggingConfiguration();

// Add targets.
// We need an async target wrapping a custom web service call back to the server.
var serverTarget = new RemoteServiceTarget();
var asyncWrapper = new AsyncTargetWrapper(serverTarget, 10000, AsyncTargetWrapperOverflowAction.Discard);
asyncWrapper.TimeToSleepBetweenBatches = (int)TimeSpan.FromSeconds(2).TotalMilliseconds;
asyncWrapper.BatchSize = 200;

// Add rules.
var rule = new LoggingRule("Company.Application.SilverlightApp.*", forLevel, asyncWrapper);
config.LoggingRules.Add(rule);

// Activate the configuration.
LogManager.Configuration = config;
LogManager.GlobalThreshold = forLevel;
}

Answer

I've implemented a fix by editing the current NLog source code.

In AsyncTargetWrapper.cs, change the FlushAsync() method from:

    protected override void FlushAsync(AsyncContinuation asyncContinuation)
    {
        this.flushAllContinuation = asyncContinuation;
    }

To:

    protected override void FlushAsync(AsyncContinuation asyncContinuation)
    {
        this.flushAllContinuation = asyncContinuation;
        this.ProcessPendingEvents(null);        // Added to make this flush synchronous.
    }