.NET 9使用Serilog
背景:
在多线程并发场景中,当线程A和线程B同时向日志文件写入数据时,由于线程调度机制的不确定性,会导致日志条目交替穿插(如A→B→A→B),如下示例图:
这种乱序现象会严重影响故障排查效率。为解决该问题,采用Serilog日志框架配合内存队列实现批处理机制:通过将各线程产生的日志暂存到线程安全的队列中,再由独立线程批量提交到持久化存储。这种设计既避免了多线程直接竞争I/O资源,又能保持业务线程的执行效率,同时通过有序批量写入确保日志的可追溯性和顺序性。
本章目标:使用Serilog日志框架实现日志的批量写入,避免多线程环境下日志穿插的问题。
1. 安装必要 NuGet 包,后续还有其他的功能配置,干脆一次性把需要的包安装好。
Install-Package Serilog
Install-Package Serilog.Sinks.File
Install-Package Serilog.Sinks.Async
Install-Package Serilog.Settings.Configuration
Install-Package Serilog.Extensions.Logging
Install-Package Serilog.Sinks.Console
2. 创建内存日志接收器(MemorySink)
public class MemorySink : ILogEventSink{private readonly ConcurrentQueue<LogEvent> _logEvents = new();public void Emit(LogEvent logEvent){_logEvents.Enqueue(logEvent); // 暂存日志到内存队列 }public IEnumerable<LogEvent> GetLogs() => _logEvents.ToArray();public void Clear() => _logEvents.Clear();}
MemorySink.Emit方法由 Serilog 框架自动调用。当在作用域内使用logger.Information()等日志方法时,Serilog 内部管道会自动将日志事件传递给所有注册的 Sink(包括MemorySink),触发其Emit方法
3. 封装作用域批量写入逻辑
public class LogBatchScope : IDisposable{private readonly MemorySink _memorySink;private readonly Logger _logger;public LogBatchScope(){_memorySink = new MemorySink();_logger = new LoggerConfiguration().WriteTo.Sink(_memorySink)
