C# 按月压缩归档日志并删除原日志
本文包含AI辅助创作内容
服务长期运行会持续生成大量 `.log` 日志文件,日积月累会快速占满磁盘空间。需要一套自动归档方案:按文件名内日期筛选日志,按月打包Zip压缩,压缩成功后删除原始日志文件。
本文基于 System.IO.Compression.ZipArchive 实现,支持增量更新Zip包,避免重复写入相同日志,包含完整异常捕获,适用于 WinForm、Windows服务、控制台程序。
⚠️ 注意:依赖 System.IO.Compression.FileSystem 程序集,.NET Framework 4.7项目需要手动添加引用。只处理文件名包含 yyyy‑MM‑dd 格式日期的log文件。
业务需求
- 根据文件名内
yyyy‑MM‑dd日期,筛选指定起止时间区间的日志文件 - 日志文件按月份分组,每个月生成一个
yyyy.MM_logs.zip归档包 - Zip包已存在时使用Update模式,判断条目是否已存在,防止重复添加日志
- 全部文件写入Zip完成之后,再删除原始log文件,防止中途异常丢失数据
- 输出处理日志,覆盖无文件、正常处理、异常报错三种场景
- 不处理当月正在写入的活跃日志,只归档上月及更早历史日志
✅ 重要原则:必须压缩全部完成后再删除原文件;禁止边压缩边删除,避免程序崩溃造成日志丢失。
所需命名空间
using System;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text.RegularExpressions;
调用示例
起始时间2016‑01‑01,结束时间取上月最后一天,跳过当月正在写入的日志:
string path = @"D:\Logs";
DateTime startDate = new DateTime(2016, 1, 1);
DateTime endDate = new DateTime(System.DateTime.Now.Year, System.DateTime.Now.Month, 1).AddDays(-1);
CompressAndDeleteLogFilesByMonth(path, startDate, endDate);
核心方法完整代码
按月份压缩归档日志,压缩成功后删除原始log文件:
/// <summary>
/// 按月份压缩归档日志文件,压缩成功后删除原log文件
/// 根据文件名中 yyyy‑MM‑dd 日期筛选文件
/// </summary>
/// <param name="folderPath">日志文件夹路径</param>
/// <param name="startDate">筛选起始日期</param>
/// <param name="endDate">筛选结束日期</param>
static void CompressAndDeleteLogFilesByMonth(string folderPath, DateTime startDate, DateTime endDate)
{
try
{
// 正则表达式用于匹配文件名中的日期
Regex dateRegex = new Regex(@"\d{4}-\d{2}-\d{2}");
// 获取指定日期范围内的所有 .log 文件
var logFiles = Directory.GetFiles(folderPath, "*.log")
.Where(file =>
{
string fileName = Path.GetFileName(file);
Match match = dateRegex.Match(fileName);
if (match.Success)
{
DateTime fileDate;
if (DateTime.TryParse(match.Value, out fileDate))
{
return fileDate >= startDate && fileDate <= endDate;
}
}
return false;
})
.ToList();
if (logFiles.Count > 0)
{
// 按月份分组
var groupedFiles = logFiles.GroupBy(file =>
{
string fileName = Path.GetFileName(file);
Match match = dateRegex.Match(fileName);
if (match.Success)
{
DateTime fileDate;
if (DateTime.TryParse(match.Value, out fileDate))
{
return new DateTime(fileDate.Year, fileDate.Month, 1);
}
}
return DateTime.MinValue;
});
foreach (var group in groupedFiles)
{
if (group.Key != DateTime.MinValue)
{
string zipFileName = Path.Combine(folderPath, $"{group.Key:yyyy.MM}_logs.zip");
bool zipExists = File.Exists(zipFileName);
ZipArchiveMode mode = zipExists ? ZipArchiveMode.Update : ZipArchiveMode.Create;
using (ZipArchive zip = ZipFile.Open(zipFileName, mode))
{
foreach (string logFile in group)
{
string entryName = Path.GetFileName(logFile);
bool needAdd = true;
if (zipExists)
{
var existEntry = zip.GetEntry(entryName);
if (existEntry != null)
{
needAdd = false;
}
}
if (needAdd)
{
zip.CreateEntryFromFile(logFile, entryName);
}
}
}
// 删除已压缩日志
foreach (string logFile in group)
{
File.Delete(logFile);
}
LogBaseWay.WriteTxt("处理日志", $"成功压缩并删除 {group.Key:yyyy.MM} 在 {Path.GetFileName(folderPath)} 目录下的 .log 文件。");
}
}
}
else
{
LogBaseWay.WriteTxt("处理日志", $"未找到 {startDate:yyyy.MM.dd} 到 {endDate:yyyy.MM.dd} 在 {Path.GetFileName(folderPath)} 目录下的 .log 文件。");
}
}
catch (Exception ex)
{
LogBaseWay.WriteTxt("处理日志", $"发生错误: {ex.Message}");
}
}
坑点与注意事项
- 程序集引用:.NET Framework 需要手动引用
System.IO.Compression.FileSystem,否则 ZipFile.Open 编译报错。 - 文件名格式依赖:正则匹配
yyyy‑MM‑dd,文件名不带日期的log直接跳过,不会处理。 - 并发风险:多线程/多进程同时操作同一个zip包,会报文件被占用异常,建议单线程定时任务执行。
- 文件夹权限:运行账号需要文件夹读、写、删除权限,权限不足抛出IO异常。
- 大日志文件:超大log压缩占用CPU与IO,定时任务避开业务高峰期。
- 正在占用的日志:代码没有判断文件是否被占用,如果日志还在被写入,File.Delete会抛出异常。
⚠️ LogBaseWay.WriteTxt 为项目内部日志工具,使用时替换为自己项目日志输出方式。
可扩展优化方向
- 增加归档zip包过期清理,自动删除N个月前的压缩包
- 增加文件占用检测,跳过正在被程序打开写入的日志文件
- 支持递归扫描子目录下的log文件
- 增加zip包大小判断,超过阈值自动拆分压缩包
- 返回处理结果对象,方便外部监控告警
小结
该归档逻辑适合 Windows服务、后台定时任务,解决日志堆积磁盘占满问题,关键点:
可以封装成定时任务,定期执行,自动完成历史日志归档,防止磁盘被日志填满。
请先 登录后发表评论 ~