Должен ли NLog очищать все сообщения в очереди в AsyncTargetWrapper при вызове Flush()?

Я хочу закрыть приложение и написать любые ожидающие сообщения журнала. Поэтому я вызываю LogManager.Flush() во время моего завершения работы. Однако я не вижу всех выписанных сообщений. Вместо этого, если я жду несколько секунд (используя Thread.Sleep()), я вижу сообщения.

После изучения кода NLog в GitHUB, я нахожу, что метод AsyncTargetWrapper.FlushAsync() предназначен только для планирования ленивого потока писем для записи всех ожидающих сообщений на следующая партия. Он не пишет сообщения журнала синхронно.

Является ли это ожидаемым поведением? Я ожидаю, что LogManager.Flush() будет синхронным, т.е. Заблокировать, пока не будут записаны все ожидающие сообщения (или превышение тайм-аута).


Код, который я использую при выключении:

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

И затем код для инициализации Nlog (это приложение Silverlight, поэтому я не использую никаких файлов конфигурации).

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

Ответ 1

Я применил исправление, отредактировав текущий исходный код NLog.

В AsyncTargetWrapper.cs измените метод FlushAsync() на:

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.
}

Ответ 2

Комментарий ligos находится на правильном пути. Метод AsyncTargetWrapper.CloseTarget() был изменен в результате NLog issue 134, где вложенный BufferingTargetWrapper не был сбрасывается при выгрузке домена.

LogManager.Shutdown() действительно заставляет AsyncTargetWrapper эффективно сливаться синхронно, но его следует использовать после LogManager.Flush(), потому что цели, такие как BufferingTargetWrapper, фактически не закрываются при закрытии. Может быть, лучше установить LogManager.Configuration = null, поскольку это выполняет флеш, а затем закрывает цели одним ударом, и конфигурация будет перезагружена (если используется файл конфигурации) в следующий раз, когда ее нужно будет использовать.

Я тестировал оба, и я пошел с последним, так как я хочу, чтобы мои записи выполнялись после того, как я правильно все понял, но учитывая, что в Silverlight я бы рекомендовал:

LogManager.Flush();
LogManager.Shutdown();

Edit

LogManager устанавливает конфигурацию в значение null для выгрузки или завершения процесса, поэтому мы не должны видеть эту проблему, если не выполняем старую версию NLog. Проблема NLog была исправлена ​​в октябре 2012 года. Протестировано без явного отключения или настройки конфигурации до нуля и может подтвердить, что вызов LogManager.Flush() достаточно.

Ответ 3

Все еще не идеально, но как насчет этого:

var reset = new ManualResetEventSlim(false);
LogManager.Flush(ex => reset.Set(), TimeSpan.FromSeconds(15));
reset.Wait(TimeSpan.FromSeconds(15));