我创建了一个文件 ILogger 提供程序。它有一个后台任务来实际写入磁盘,因此
Log()
调用不会等待磁盘写入完成。
创建/管理此后台任务的最佳方式是什么?我可以把它放在
Task.Run()
中,然后我就忘记了。但这让我觉得有问题。
此外,永远不会调用
ILogger.Provider.Dispose()
。我希望处理该任务,以便它可以关闭已打开的文件。
而且,当调用 Dispose() 时,我需要结束
Task.Run()
内的循环。在这种情况下,只设置一个布尔值会更好吗?或者我应该使用 CancellationToken 吗?
您想使用线程安全队列。记录器将日志消息放入队列中,一个或多个线程/任务从队列中选取消息并将其写入磁盘。 Dispose 应该使队列停止接受新消息,并等待写入任何已排队的消息。
有很多方法可以编写这样的队列,一个非常简单的替代方法是使用阻塞集合:
public class QueueExample<T> : IAsyncDisposable
{
private readonly Task task;
private readonly BlockingCollection<T> queue = new();
public QueueExample() => task = Task.Run(DoProcessing);
public void Add(T item) => queue.Add(item);
private void DoProcessing()
{
foreach (var item in queue.GetConsumingEnumerable())
{
// do processing
}
}
public async ValueTask DisposeAsync()
{
queue.CompleteAdding();
await task;
}
}
一旦调用
CompleteAdding
并且所有项目都已处理完毕,循环就会停止。
但我的建议是使用现有的日志框架。这将为您做这样的缓冲,以及在记录时有用的许多其他事情。
我对 nLog 很有经验,而且设置起来相当容易。添加 nuget 依赖项并将配置放入 app.config 文件中,这样的内容至少适用于 .net 框架:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog" />
</configSections>
<nlog>
<targets async="true">
<target name="fileTarget"
xsi:type="File"
keepFileOpen ="true"
openFileCacheTimeout ="30"
fileName=".\Logs\Log-${date}.txt"/>
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="fileTarget" />
</rules>
</nlog>
...
并像这样创建你的记录器
private static readonly Logger log = LogManager.GetCurrentClassLogger();