nlog按日期滚动最直接,需设archiveevery="day"且enablearchivefilecompression="false";serilog按大小滚动更可控,须设rollonfilesizelimit=true和filesizelimitbytes;手动实现需避免filestream锁与时钟误差。

用 NLog 配置按日期滚动最直接
绝大多数 C# 项目不需要手写滚动逻辑,NLog 内置的 FileTarget 支持开箱即用的日期滚动。关键不是“能不能”,而是“怎么配不踩坑”。
常见错误是只设 archiveEvery="Day" 却没关掉 enableArchiveFileCompression="false"(默认为 true),结果每天生成一堆 .zip 文件而非纯文本,排查日志时反而更麻烦。
推荐配置片段:
<target xsi:type="File" name="file" fileName="${basedir}/logs/app.log"
archiveFileName="${basedir}/logs/app.{#}.log"
archiveEvery="Day"
archiveNumbering="Rolling"
maxArchiveFiles="30"
enableArchiveFileCompression="false" />-
archiveNumbering="Rolling":旧文件编号递增(app.1.log,app.2.log…),比Date模式更易脚本清理 -
maxArchiveFiles="30":硬性限制归档数,否则磁盘可能被撑爆——这是生产环境最容易被忽略的点 - 路径中避免使用
${date:format=yyyy-MM-dd}做fileName,会导致日志写入失败(NLog 不支持在主文件名里动态插日期)
用 Serilog + File sink 按大小切分更可靠
按大小滚动比按日期更可控,尤其适合高吞吐服务。Serilog 的 Serilog.Sinks.File 默认就支持,但默认行为容易误导人。
它不叫“按大小滚动”,而叫“按文件大小限制 + 自动重命名”。关键参数是 rollingInterval 和 retainedFileCountLimit,但真正触发切割的是 fileSizeLimitBytes。
示例代码:
Log.Logger = new LoggerConfiguration()
.WriteTo.File("logs/app.log",
fileSizeLimitBytes: 10_485_760, // 10MB
rollOnFileSizeLimit: true,
retainedFileCountLimit: 7,
rollingInterval: RollingInterval.Day) // 这个值其实不影响大小滚动,仅用于命名后缀
.CreateLogger();-
rollOnFileSizeLimit: true必须显式开启,否则即使设了fileSizeLimitBytes也无效 -
retainedFileCountLimit: 7是保留的**总文件数**(当前 + 归档),不是“最多存 7 天”——这点文档写得模糊,实际测试会发现第 8 个归档产生时,最老的就被删了 - 归档文件名形如
app.log.20240520.txt或app.log.1.txt,取决于rollingInterval设置;若只想按大小切、不掺日期,设成RollingInterval.Infinite即可
手动实现滚动要绕开 FileStream 锁和时间判断误差
真要自己写(比如嵌入式场景或规避第三方依赖),核心难点不在逻辑,而在两个地方:FileStream 的独占锁导致无法安全重命名正在写的文件;以及服务器时钟跳变让“是否跨天”判断出错。
解决方案不是修判断,而是换思路:
- 用
File.Move()前先stream.Dispose(),别试图在流开着时移动文件 - 不依赖
DateTime.Now.Date判断是否换日,改用“记录上一次滚动时间戳 + 24 小时检查”,避免 NTP 校时或夏令时导致误切 - 大小判断别用
FileInfo.Length(可能因缓存不准),而是在每次WriteLine后累加已写字节数,更实时
一句话:手动滚动不是“写个计时器+判断大小”,而是“管好文件句柄生命周期 + 用增量计数替代查询”。
log4net 的 RollingFileAppender 容易卡在 StaticLogFileName
老项目还在用 log4net 的话,RollingFileAppender 的 StaticLogFileName="false" 是开关,但默认是 true——这意味着即使你配了 datePattern,它也只写一个文件,不滚动。
典型错误配置:
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender"> <file value="logs/app.log" /> <datePattern value=".yyyy-MM-dd" /> <rollingStyle value="Date" /> </appender>
缺了 <staticlogfilename value="false"></staticlogfilename>,上面配置完全无效。加上之后还要注意:datePattern 中的 . 会被当字面量,所以 .yyyy-MM-dd 生成的是 app.log.2024-05-20,而不是 app.2024-05-20.log。
真正想按日期生成 app.YYYY-MM-DD.log,得把 <file></file> 设为空,靠 datePattern 构建完整文件名,且必须配合 staticLogFileName="false"。
滚动逻辑本身不复杂,难的是边界:文件锁、时钟漂移、归档清理策略、压缩与可读性的权衡。选库时别只看“支持滚动”,要看它默认行为是否贴合你的运维习惯。









