Должен ли 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));
Ответ 4
Незначительно украден из: Буферизация сообщений журнала в NLog и вручную очищает их от таргетинга
LogManager.Configuration.AllTargets
.OfType<BufferingTargetWrapper>()
.ToList()
.ForEach(b => b.Flush(e =>
{
//do nothing here
}));