我有一个使用 Logger 的 C# 项目。
为了提高性能,这个 Logger 会将日志信息添加到一个等待队列中,由另一个线程处理它,包括将它写入控制台和日志文件。
不幸的是,它有一个大问题:如果进程终止得太快以至于记录器的速度不足以跟上和处理队列中的其余日志信息,它们将永久丢失.
更清楚的是,这是我的日志处理线程的主要代码:
public static void Info(string content, string? sender = null)
=> qlog.Enqueue(new LogDetail(content, LogLevel.Information, sender));
...
private static ConcurrentQueue<LogDetail> qlog = new();
public static int RefreshLogTicks { get; set; }
private static async Task BackgroundUpdate()
{
if (!qlog.TryDequeue(out LogDetail _log))
{
await Task.Delay(RefreshLogTicks);
await Task.Run(BackgroundUpdate);
return;
}
List<string> logs = new(qlog.Count + 1);
logs.Add(WriteLog(_log));
while (qlog.TryDequeue(out LogDetail log))
{
logs.Add(WriteLog(log));
}
ConsoleWrapper.WriteLine(logs);
await Task.Delay(RefreshLogTicks);
await Task.Run(BackgroundUpdate);
}
(如果需要,完整代码在GitHub上开源)
然后如果我执行这样的代码:
Console.WriteLine("Hello, ");
Log.Info("Logger!", "Hello");
Console.WriteLine("World!");
Environment.Exit(0);
然后我得到这样的输出:(至少在我的带有 2 核 CPU 的 sloooow 笔记本电脑上)
Hello,
World!
如果我应用一个简单的更改:
Console.WriteLine("Hello, ");
Log.Info("Logger!", "Hello");
Console.WriteLine("World!");
Console.ReadLine();
Environment.Exit(0);
然后记录器输出将恢复:
Hello,
World!
21:28:17 <Info:Hello> Logger!
所以,我想知道是否有一种方法可以保证只有当队列为空时进程才会终止?还是让进程等到 handle 进程结束?
(我知道如果有人杀死我无法停止退出过程,所以下面的“结束”指的是正常退出,例如
Environment.Exit()
或Application.Exit()
。)
请注意,我并不期望像提供方法那样的实现
Stop()
并要求在关闭之前调用它,因为我认为代码用户不会总是正确地调用它——正如上面提到的,如果额外调用是需要,Console.ReadLine()
似乎简单得多。
如果不可能,也请告诉我。非常感谢!
在退出之前,关闭并刷新记录器:
Log.CloseAndFlush();