如何配置log4net删除N天前日志及按日期和大小滚动日志?
解决log4net日志滚动与旧日志清理的问题
一、配置RollingFileAppender实现日期+大小滚动,每日保留10个1MB日志文件
结合官方示例,我帮你补全并完善了配置,实现同时按日期和文件大小滚动,且每天仅保留10个1MB的日志文件:
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender"> <!-- 日志文件基础命名 --> <file value="logfile" /> <!-- 开启追加模式,不覆盖已有日志 --> <appendToFile value="true" /> <!-- 核心设置:复合滚动模式,同时按日期和文件大小触发滚动 --> <rollingStyle value="Composite" /> <!-- 日期后缀格式,生成如 logfile.20240520.log 的文件名 --> <datePattern value=".yyyyMMdd.log" /> <!-- 同一日期下最多保留10个大小备份文件 --> <maxSizeRollBackups value="10" /> <!-- 单个日志文件的上限大小(1MB) --> <maximumFileSize value="1MB" /> <!-- 禁用静态文件名,让log4net自动生成带日期/序号的动态文件名 --> <staticLogFileName value="false" /> <!-- 保留日志文件的扩展名在末尾,避免出现 logfile.log.20240520 这类格式 --> <preserveLogFileNameExtension value="true" /> </appender>
关键配置说明:
rollingStyle="Composite":这是实现双维度滚动的核心,让日志既会在每天零点自动切分,也会在单个文件达到1MB时立即滚动。maxSizeRollBackups="10":限制同一天内最多保留10个备份文件,超过数量的旧文件会被自动覆盖。datePattern:定义日期后缀格式,确保每天的日志文件有独立的标识,方便后续清理。
二、实现删除早于N天的日志文件
log4net的RollingFileAppender本身没有自动删除旧日期日志的功能,需要我们手动实现清理逻辑,这里提供两种实用方案:
方案1:应用启动时一次性清理
在应用初始化的代码段(比如ASP.NET的Global.asax、Console程序的Main方法开头)添加清理逻辑,启动时删除指定天数前的日志:
using System.IO; using System; public static void CleanExpiredLogs(string logDir, int daysToRetain) { if (!Directory.Exists(logDir)) return; var cutoffDate = DateTime.Now.AddDays(-daysToRetain); // 匹配所有带日期/序号后缀的日志文件 var logFiles = Directory.GetFiles(logDir, "logfile.*.log"); foreach (var filePath in logFiles) { var fileInfo = new FileInfo(filePath); // 按文件最后写入时间判断是否过期 if (fileInfo.LastWriteTime < cutoffDate) { try { File.Delete(filePath); } catch (IOException ex) { // 处理文件被占用的情况(比如日志正在写入) Console.WriteLine($"无法删除过期日志 {filePath}:{ex.Message}"); } } } } // 调用示例:保留最近7天的日志 CleanExpiredLogs(AppDomain.CurrentDomain.BaseDirectory, 7);
方案2:定时任务定期清理(适合长期运行的服务)
如果你的应用是Windows服务、ASP.NET Core后台服务这类长期运行的程序,可以添加定时任务,每天自动清理一次旧日志。以ASP.NET Core为例,用IHostedService实现:
using Microsoft.Extensions.Hosting; using System.Threading; using System.Threading.Tasks; using System.IO; using System; public class LogCleanupHostedService : IHostedService, IDisposable { private Timer _cleanupTimer; private readonly string _logDirectory; private readonly int _daysToKeep; public LogCleanupHostedService(string logDir, int daysToKeep) { _logDirectory = logDir; _daysToKeep = daysToKeep; } public Task StartAsync(CancellationToken cancellationToken) { // 设置每天凌晨2点执行清理 var now = DateTime.Now; var firstRun = new DateTime(now.Year, now.Month, now.Day, 2, 0, 0); if (now > firstRun) firstRun = firstRun.AddDays(1); var initialDelay = firstRun - now; _cleanupTimer = new Timer(ExecuteCleanup, null, initialDelay, TimeSpan.FromDays(1)); return Task.CompletedTask; } private void ExecuteCleanup(object state) { CleanExpiredLogs(_logDirectory, _daysToKeep); } public Task StopAsync(CancellationToken cancellationToken) { _cleanupTimer?.Change(Timeout.Infinite, 0); return Task.CompletedTask; } public void Dispose() { _cleanupTimer?.Dispose(); } // 复用方案1的清理逻辑 private void CleanExpiredLogs(string logDir, int daysToRetain) { if (!Directory.Exists(logDir)) return; var cutoffDate = DateTime.Now.AddDays(-daysToRetain); var logFiles = Directory.GetFiles(logDir, "logfile.*.log"); foreach (var filePath in logFiles) { var fileInfo = new FileInfo(filePath); if (fileInfo.LastWriteTime < cutoffDate) { try { File.Delete(filePath); } catch (IOException ex) { Console.WriteLine($"清理日志失败:{ex.Message}"); } } } } } // 在Program.cs中注册服务 builder.Services.AddHostedService(provider => new LogCleanupHostedService(AppDomain.CurrentDomain.BaseDirectory, 7));
注意事项:
- 清理时务必捕获
IOException,避免因日志文件被log4net占用导致程序崩溃。 - 如果你的日志文件名格式和示例不同,需要调整
Directory.GetFiles的搜索通配符。
内容的提问来源于stack exchange,提问作者CodeZila
相关产品推荐
相关产品推荐

