【发布时间】:2020-01-16 15:49:03
【问题描述】:
在这个线程中 - Should NLog flush all queued messages in the AsyncTargetWrapper when Flush() is called? - 我读到“LogManager 在域卸载或进程退出时将配置设置为空”(请参阅第一个答案中的编辑部分)。据我了解,这应该会导致所有挂起的日志条目都写入已注册的目标。然而,在用FileTarget 包裹AsyncTargetWrapper 进行测试后,这并不成立。我在 GitHub 上创建了一个最小的 repro - https://github.com/PaloMraz/NLogMultiProcessTargetsSample,它的工作原理如下:
LogLib 是一个 .netstandard2.0 库,它引用 NLog 4.6.8 NuGet 包并公开一个 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