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

Pal*_*raz 1 .net c# logging nlog multiprocessing

在此线程中 -当调用 Flush() 时,NLog 是否应该刷新 AsyncTargetWrapper 中的所有排队消息?- 我读到 \xe2\x80\x9eLogManager在域卸载或进程退出\xe2\x80\x9c 时将配置设置为 null(请参阅第一个答案中的编辑部分)。根据我的理解,这应该会导致所有待处理的日志条目被写入已注册的目标。然而,在FileTarget用wrapped with进行测试后AsyncTargetWrapper,这并不成立。我在 GitHub 上创建了一个最小的重现 - https://github.com/PaloMraz/NLogMultiProcessTargetsSample,其工作原理如下:

\n\n

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

\n\n
public class CompositeLogger\n{\n  private readonly ILogger _logger;\n\n  public CompositeLogger(string logFilePath)\n  {\n    var fileTarget = new FileTarget("file")\n    {\n      FileName = logFilePath,\n      AutoFlush = true\n    };\n    var asyncTargetWrapper = new AsyncTargetWrapper("async", fileTarget)\n    {\n      OverflowAction = AsyncTargetWrapperOverflowAction.Discard\n    };\n\n    var config = new LoggingConfiguration();\n    config.AddTarget(asyncTargetWrapper);\n    config.AddRuleForAllLevels(asyncTargetWrapper);\n    LogManager.Configuration = config;\n\n    this._logger = LogManager.GetLogger("Default");\n  }\n\n  public void Log(string message) => this._logger.Trace(message);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
public static class Program\n{\n  public const int LogWritesCount = 10;\n  public static readonly TimeSpan DelayBetweenLogWrites = TimeSpan.FromMilliseconds(25);\n\n  static async Task Main(string[] args)\n  {\n    string logFilePath = args.FirstOrDefault();\n    if (string.IsNullOrWhiteSpace(logFilePath))\n    {\n      throw new InvalidOperationException("Must specify logging file path as an argument.");\n    }\n\n    logFilePath = Path.GetFullPath(logFilePath);\n    Process currentProcess = Process.GetCurrentProcess();\n    var logger = new CompositeLogger(logFilePath);\n    for(int i = 0; i < LogWritesCount; i++)\n    {\n      logger.Log($"Message from {currentProcess.ProcessName}#{currentProcess.Id} at {DateTimeOffset.Now:O}");\n      await Task.Delay(DelayBetweenLogWrites);\n    }\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
[Fact]\npublic async Task LaunchMultipleRunners()\n{\n  string logFilePath = Path.GetTempFileName();\n  using var ensureLogFileDisposed = new Nito.Disposables.AnonymousDisposable(() => File.Delete(logFilePath));\n\n  string logConsoleRunnerAppExePath = Path.GetFullPath(\n    Path.Combine(\n      Path.GetDirectoryName(this.GetType().Assembly.Location),\n      @"..\\..\\..\\..\\LogConsoleRunner\\bin\\Debug\\LogConsoleRunner.exe"));      \n  var startInfo = new ProcessStartInfo(logConsoleRunnerAppExePath)\n  {\n    Arguments = logFilePath,\n    UseShellExecute = false\n  };\n  const int LaunchProcessCount = 10;\n  Process[] processes = Enumerable\n    .Range(0, LaunchProcessCount)\n    .Select(i => Process.Start(startInfo))\n    .ToArray();\n  while (!processes.All(p => p.HasExited))\n  {\n    await Task.Delay(LogConsoleRunner.Program.DelayBetweenLogWrites);\n  }\n\n  string[] lines = File.ReadAllLines(logFilePath);\n  Assert.Equal(LaunchProcessCount * LogConsoleRunner.Program.LogWritesCount, lines.Length);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

最后一行Assert.Equal总是失败,因为目标文件写入的行数始终少于预期计数(即 100)。在我的计算机上,每次运行它都会在 96 \xe2\x80\x93 99 之间变化,但它从不包含所有内容100 行。

\n\n

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

\n

Jul*_*ian 6

只需LogManager.Shutdown()Main. 它将刷新所有待处理的日志事件。阅读更多

旁注:如果您在刷新后需要 NLog,那么您可以使用 来代替 shutdown LogManager.Flush()

  • 为了使其更好,请在程序退出之前调用“LogManager.Shutdown()”作为最后一条语句(它将刷新并关闭计时器和线程)。这将避免非 Windows 平台(Linux、Android 等)上的分段错误。另请参阅:https://github.com/NLog/NLog/wiki/Tutorial#5-remember-to-flush (4认同)
  • 谢谢!确实这样更好。更新了答案 (2认同)