NLog不会在进程退出时刷新所有日志条目

问题描述 投票:0回答:2

在此线程中-Should NLog flush all queued messages in the AsyncTargetWrapper when Flush() is called?-我读到“ LogManager在域卸载或进程退出时将配置设置为null”(请参阅​​第一个答案的“编辑”部分)。以我的理解,这应该导致所有待处理的日志条目都写入已注册的目标。但是,在用FileTarget包裹AsyncTargetWrapper进行测试之后,这并不成立。我已经在GitHub-https://github.com/PaloMraz/NLogMultiProcessTargetsSample上创建了一个最小的propro,其工作方式如下:

[LogLib是一个引用.netstandard2.0 4.6.8 NuGet程序包并公开以编程方式配置NLog目标的CompositeLogger类的NLog库:

public class CompositeLogger
{
  private readonly ILogger _logger;

  public CompositeLogger(string logFilePath)
  {
    var fileTarget = new FileTarget("file")
    {
      FileName = logFilePath,
      AutoFlush = true
    };
    var asyncTargetWrapper = new AsyncTargetWrapper("async", fileTarget)
    {
      OverflowAction = AsyncTargetWrapperOverflowAction.Discard
    };

    var config = new LoggingConfiguration();
    config.AddTarget(asyncTargetWrapper);
    config.AddRuleForAllLevels(asyncTargetWrapper);
    LogManager.Configuration = config;

    this._logger = LogManager.GetLogger("Default");
  }

  public void Log(string message) => this._logger.Trace(message);
}

[LogConsoleRunner是一个.NET Framework 4.8控制台应用程序,它使用LogLib.CompositeLogger将指定数量的日志消息写入文件(指定为命令行参数),两次写入之间的延迟很短:

public static class Program
{
  public const int LogWritesCount = 10;
  public static readonly TimeSpan DelayBetweenLogWrites = TimeSpan.FromMilliseconds(25);

  static async Task Main(string[] args)
  {
    string logFilePath = args.FirstOrDefault();
    if (string.IsNullOrWhiteSpace(logFilePath))
    {
      throw new InvalidOperationException("Must specify logging file path as an argument.");
    }

    logFilePath = Path.GetFullPath(logFilePath);
    Process currentProcess = Process.GetCurrentProcess();
    var logger = new CompositeLogger(logFilePath);
    for(int i = 0; i < LogWritesCount; i++)
    {
      logger.Log($"Message from {currentProcess.ProcessName}#{currentProcess.Id} at {DateTimeOffset.Now:O}");
      await Task.Delay(DelayBetweenLogWrites);
    }
  }
}

最后,LogTest是一个XUnit测试程序集,其中一个测试启动十个LogConsoleRunner实例写入同一日志文件:

[Fact]
public async Task LaunchMultipleRunners()
{
  string logFilePath = Path.GetTempFileName();
  using var ensureLogFileDisposed = new Nito.Disposables.AnonymousDisposable(() => File.Delete(logFilePath));

  string logConsoleRunnerAppExePath = Path.GetFullPath(
    Path.Combine(
      Path.GetDirectoryName(this.GetType().Assembly.Location),
      @"..\..\..\..\LogConsoleRunner\bin\Debug\LogConsoleRunner.exe"));      
  var startInfo = new ProcessStartInfo(logConsoleRunnerAppExePath)
  {
    Arguments = logFilePath,
    UseShellExecute = false
  };
  const int LaunchProcessCount = 10;
  Process[] processes = Enumerable
    .Range(0, LaunchProcessCount)
    .Select(i => Process.Start(startInfo))
    .ToArray();
  while (!processes.All(p => p.HasExited))
  {
    await Task.Delay(LogConsoleRunner.Program.DelayBetweenLogWrites);
  }

  string[] lines = File.ReadAllLines(logFilePath);
  Assert.Equal(LaunchProcessCount * LogConsoleRunner.Program.LogWritesCount, lines.Length);
}

最后一行的Assert.Equal总是失败,因为目标文件写的行总是少于期望的计数,即100。在我的机器上,每次运行它在96 – 99之间变化,但是它从不包含所有100行。

我的问题:我应该如何配置NLog以确保在所有进程退出之后,所有待处理的日志条目都被写入目标日志文件?

c# .net logging multiprocessing nlog
2个回答
2
投票

只需致电LogManager.Shutdown()LogManager.Shutdown()的末尾。它将刷新所有未决的日志事件。 Main

旁注:如果刷新后需要NLog,则可以使用Read more代替关机。


0
投票

已经查看了示例代码,并且您有多个进程写入相同的文件名。

认为您是性能和正确性之间折衷的受害者。

[当多个进程同时写入同一文件时,则需要进行一些锁定以进行协调。默认情况下,NLog使用最兼容的模式(KeepFileOpen = false),这是来自操作系统的文件锁(适用于大多数平台)。

从操作系统进行文件锁定是不公平的,并且当有两个以上的进程写入同一文件时,该文件无法缩放。当一个进程试图打开一个文件,而另一个进程当前正在使用该文件时,将引发异常。

NLog尝试通过重试错误(concurrentWriteAttempts = 10)并随机分配重试前等待的时间来处理这些异常。这对于2个进程来说还可以,但是当您开始增加进程数时,则增加了一个进程连续10次失败的可能性。在最后一次重试之后,NLog会丢弃LogEvent(可能是您看到的内容)。

KeepFileOpen = false很慢(300次写入/秒),当与retry-logic结合使用时,它将变得非常慢。但是,当允许批处理时,通过使用AsyncWrapper几乎可以消除性能下降。但是现在,当重试计数用完时,整个批次可能会丢失。

而不是依靠操作系统文件锁,而是可以依靠NLog使用全局互斥锁进行进程间通信。通过LogManager.Flush()LogManager.Flush()启用此模式。而不是300次写入/秒,然后变为100.000次写入/秒,并且锁定机制更加公平,因此无需重试。并非所有平台都支持此模式,但在Windows上的.NET 4.8(在Linux上为NetCore2)上应该能很好地工作。

另请参见:KeepFileOpen=True

© www.soinside.com 2019 - 2024. All rights reserved.