Serilog Async Sink关闭时未刷缓冲区至磁盘,日志丢失求助
我之前也碰到过一模一样的问题——用Async Sink配合开启缓冲的File Sink时,程序关闭阶段总有日志丢了,明明调用了Log.CloseAndFlush()却没生效,去掉Async就一切正常。这本质是Async Sink的异步队列和File Sink的缓冲区在程序退出时没来得及完成收尾,下面是几个针对性的解决方法:
1. 给Async Sink开启队列清空等待
Serilog的Async Sink默认在关闭时不会等待队列里的所有日志事件处理完就返回了,你可以在配置里显式加上waitForQueueToEmpty: true,让Log.CloseAndFlush()等队列完全处理完再结束:
修改appsettings.Development.json里的Async配置:
{ "Serilog": { "WriteTo": [ { "Name": "Async", "Args": { "waitForQueueToEmpty": true, // 新增这个参数,强制等待队列清空 "configure": [ // 你的Console和File Sink配置保持不变 ] } } ] } }
这个参数会让Async Sink在关闭时阻塞,直到所有待处理的日志都传递给下游的File Sink,从根源上避免队列里的日志被丢弃。
2. 优化File Sink的缓冲策略
既然你开了buffered: true来提升性能,那可以给File Sink加个定期刷新的配置,哪怕程序突然退出,最近一段时间的日志也能提前刷到磁盘:
{ "Name": "File", "Args": { "path": "logs/log.txt", "formatter": "Serilog.Formatting.Json.JsonFormatter", "rollingInterval": "Day", "retainedFileCountLimit": 7, "buffered": true, "flushToDiskInterval": 1 // 每隔1秒主动刷新一次缓冲区 } }
如果日志丢失情况特别严重,也可以暂时把buffered改成false(代价是写日志的性能会稍降),先验证问题是否解决。
3. 确保ASP.NET Core关闭流程正确触发
有时候用WebHost.Run()这种阻塞式运行时,程序突然关闭(比如Ctrl+C)可能会跳过部分异步清理逻辑,换成异步运行方式会更稳妥:
修改Program.cs的Main方法为异步:
public static async Task Main(string[] args) { Log.Logger = new LoggerConfiguration() .ReadFrom.Configuration(Configuration) .CreateLogger(); try { Log.Information("Starting the web host"); var host = CreateWebHostBuilder(args).Build(); await host.RunAsync(); // 用RunAsync替代Run,支持优雅关闭 Log.Information("Shutting down the web host"); } catch (Exception ex) { Log.Fatal(ex, "Host terminated unexpectedly"); } finally { Log.Information("Closing Log"); Log.CloseAndFlush(); } }
这样Web主机在收到关闭信号时,会完整执行所有异步清理步骤,包括Serilog的日志刷新。
4. 临时 workaround:添加短暂延迟(不推荐)
如果上面的配置都没起效,你可以在finally块里加个短暂延迟,给Async Sink和File Sink足够的时间处理剩余日志:
finally { Log.Information("Closing Log"); await Task.Delay(500); // 等待500毫秒,给队列处理时间 Log.CloseAndFlush(); }
不过这方法比较hack,优先用前面的配置修改方案。
内容的提问来源于stack exchange,提问作者Bill

