I have a C# project using a Logger.
To improve performance, this Logger will add the log info to a waiting queue, and another thread will handle it, including writing it to the Console and the log file.
Unfortunately, it has a big problem: if the process is terminating so fast that the logger isn't fast enough to keep up and handle the rest of log infos in the queue, THEY WILL BE PERMANENTLY LOST.
More clearly, this is the main code of my logging handle thread:
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);
}
(If needed, the full code is open source on GitHub)
Then if I execute code like this:
Console.WriteLine("Hello, ");
Log.Info("Logger!", "Hello");
Console.WriteLine("World!");
Environment.Exit(0);
Then I get an output as this: (at least on my sloooow laptop with a 2-core CPU)
Hello,
World!
If I apply a simple change:
Console.WriteLine("Hello, ");
Log.Info("Logger!", "Hello");
Console.WriteLine("World!");
Console.ReadLine();
Environment.Exit(0);
Then the logger output will recover:
Hello,
World!
21:28:17 <Info:Hello> Logger!
So, I wonder if there's a way to guarantee that the process will terminate only when the queue is Empty? Or to make the process wait until the handle process ended?
(I know that I can't stop the exit process if someone kills it, so "end" below refers to a normal exit, e.g. Environment.Exit()
or Application.Exit()
.)
Notice that I aren't expecting an implement like providing a method Stop()
and demand invoking it before closing because I don't think the code users will always correctly invoke it - and as it is mentioned above, if an extra invoke is needed, Console.ReadLine()
seems far more simple.
If it's impossible, please tell me as well. Thanks a lot!