Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# NLog不会在进程退出时刷新所有日志条目_C#_.net_Logging_Multiprocessing_Nlog - Fatal编程技术网

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

C# NLog不会在进程退出时刷新所有日志条目,c#,.net,logging,multiprocessing,nlog,C#,.net,Logging,Multiprocessing,Nlog,在此线程中,我看到“LogManager”在域卸载或进程退出时将配置设置为null“请参阅第一个答案中的编辑部分。据我所知,这将导致所有挂起的日志条目写入注册的目标。但是,在使用AsyncTargetTrapper包装的FileTarget进行测试后,这不成立。我在GitHub-上创建了一个最小的复制,其工作原理如下: LogLib是一个.netstandard2.0库,引用NLog 4.6.8 NuGet包并公开一个CompositeLogger类,该类通过编程方式配置NLog目标: LogC

在此线程中,我看到“LogManager”在域卸载或进程退出时将配置设置为null“请参阅第一个答案中的编辑部分。据我所知,这将导致所有挂起的日志条目写入注册的目标。但是,在使用AsyncTargetTrapper包装的FileTarget进行测试后,这不成立。我在GitHub-上创建了一个最小的复制,其工作原理如下:

LogLib是一个.netstandard2.0库,引用NLog 4.6.8 NuGet包并公开一个CompositeLogger类,该类通过编程方式配置NLog目标:

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);
    }
  }
}
最后一行上的Assert.Equal总是失败,因为目标文件写入的行数总是少于预期的计数,即100。在我的计算机上,每次运行都会在96–99之间变化,但它从不包含所有100行

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

只需调用 它将刷新所有挂起的日志事件

旁注:如果您在刷新后需要NLog,则可以使用而不是关机。

只需拨打电话即可 它将刷新所有挂起的日志事件


旁注:如果刷新后需要NLog,则可以使用而不是关机。

查看了示例代码,您有多个进程写入同一文件名

认为您是性能和正确性之间妥协的受害者

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

来自操作系统的文件锁定是不公平的,当有两个以上的进程写入同一个文件时,文件锁定无法扩展。当一个进程尝试打开另一个进程当前正在使用的文件时,将引发异常

NLog试图通过在错误ConcurrentWriteAttents=10时重试,并随机化重试前的等待时间来处理这些异常。这对两个进程都有效,但当您开始增加进程数时,则会增加一个进程连续10次倒霉的机会。在最后一次重试后,NLog丢弃日志事件(可能是您看到的)

KeepFileOpen=false的写入速度为每秒300次,如果与重试逻辑结合使用,则速度会变得非常慢。但是通过在允许批处理时使用AsyncWrapper,它几乎可以消除性能影响。但是现在,当重试计数用完时,整个批处理可能会丢失

与依赖操作系统文件锁不同,您可以依赖NLog使用全局互斥进行进程间通信。此模式通过KeepFileOpen=True和ConcurrentWrites=True启用。与300次写入/秒相比,它变为100.000次写入/秒,锁定机制更公平,因此无需重试。并非所有平台支持这种模式,但在Windows上的.NET 4.8和Linux上的NetCore2上应该工作得很好


另请参见:

已经查看了示例代码,您有多个进程正在写入同一文件名

认为您是性能和正确性之间妥协的受害者

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

来自操作系统的文件锁定是不公平的,当有两个以上的进程写入同一个文件时,文件锁定无法扩展。当一个进程尝试打开另一个进程当前正在使用的文件时,将引发异常

NLog试图通过在错误ConcurrentWriteAttents=10时重试,并随机化重试前的等待时间来处理这些异常。这对两个进程都有效,但当您开始增加进程数时,则会增加一个进程连续10次倒霉的机会。在最后一次重试后,NLog丢弃日志事件(可能是您看到的)

KeepFileOpen=false的写入速度为每秒300次,如果与重试逻辑结合使用,则速度会变得非常慢。但是通过在允许批处理时使用AsyncWrapper,它几乎可以消除性能影响。但是现在,当重试计数用完时,整个批处理可能会丢失

与依赖操作系统文件锁不同,您可以依赖NLog使用glob进行进程间通信 阿尔互斥体。此模式在KeepFileOpen=True和ConcurrentWrites=True时启用。它不再是300次写入/秒,而是100.000次写入/秒,并且锁定机制更加公平,因此无需重试。并非所有平台都支持这种模式,但在Windows上的.NET 4.8和Linux上的NetCore2上应该可以很好地工作


另请参阅:

若要使其更好,请调用LogManager。Shutdown作为程序退出前的最后一条语句,它将刷新并关闭计时器和线程。这将避免在非Windows平台Linux、Android等上出现分段错误。。另见:谢谢!那确实更好。更新了应答器在此提交中添加了LogManager.Shutdown调用:-它仍然不工作,例如,在所有LogConsoleRunner.exe实例终止后,某些日志项仍然丢失。为了使其更好,请调用LogManager.Shutdown作为程序退出前的最后一条语句,它将刷新并关闭计时器和线程。这将避免在非Windows平台Linux、Android等上出现分段错误。。另见:谢谢!那确实更好。更新了应答器在此提交中添加了LogManager.Shutdown调用:-它仍然不起作用,例如,在所有LogConsoleRunner.exe实例终止后,一些日志条目仍然丢失。非常感谢您的解释!实际上,建议的FileTarget设置解决了并发写入问题:KeepFileOpen=true,ConcurrentWrites=true。我还更新了GitHub示例项目供其他人查看…非常感谢您的解释!实际上,建议的FileTarget设置解决了并发写入问题:KeepFileOpen=true,ConcurrentWrites=true。我还更新了GitHub示例项目以供其他人查看。。。
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);
    }
  }
}
[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);
}