You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
haoliang-net/src/CncCollector/Jobs/LogArchiveJob.cs

152 lines
4.9 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.IO;
using log4net;
namespace CncCollector.Jobs
{
/// <summary>
/// 日志文件归档任务。
/// 每天凌晨将前一天的日志文件移动到按日期命名的子目录中。
/// 目录结构示例:
/// logs\collector.log ← 当前在写的运行日志
/// logs\collector_error.log ← 当前在写的错误日志
/// logs\2026-05-07\collector.log
/// logs\2026-05-07\collector_error.log
/// logs\2026-05-06\collector.log
/// logs\2026-05-06\collector_error.log
/// 保留天数外的旧目录自动清理。
/// </summary>
public class LogArchiveJob
{
private static readonly ILog _log = LogManager.GetLogger(typeof(LogArchiveJob));
/// <summary>
/// 日志保留天数
/// </summary>
private readonly int _retainDays;
/// <summary>
/// 日志根目录相对于BaseDirectory
/// </summary>
private readonly string _logsDir;
public LogArchiveJob(int retainDays = 30)
{
_retainDays = retainDays;
_logsDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs");
}
/// <summary>
/// 执行归档:把当前日志文件复制到昨天的日期目录,然后截断当前文件。
/// 应在每天凌晨00:00左右调用。
/// </summary>
public void Execute()
{
try
{
if (!Directory.Exists(_logsDir))
{
return;
}
var yesterday = DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd");
var archiveDir = Path.Combine(_logsDir, yesterday);
// 归档每个日志文件
ArchiveLogFile("collector.log", archiveDir);
ArchiveLogFile("collector_error.log", archiveDir);
// 清理过期目录
CleanupOldDirectories();
_log.Info($"日志归档完成({yesterday}");
}
catch (Exception ex)
{
_log.Error("日志归档失败", ex);
}
}
/// <summary>
/// 归档单个日志文件到日期目录
/// </summary>
private void ArchiveLogFile(string fileName, string archiveDir)
{
var sourceFile = Path.Combine(_logsDir, fileName);
if (!File.Exists(sourceFile))
{
return;
}
var fileInfo = new FileInfo(sourceFile);
// 文件为空或太新最后写入在1小时内则跳过
// 避免归档正在活跃写入的当天日志
if (fileInfo.Length == 0)
{
return;
}
// 创建日期目录
if (!Directory.Exists(archiveDir))
{
Directory.CreateDirectory(archiveDir);
}
var destFile = Path.Combine(archiveDir, fileName);
// 如果目标已存在,追加序号
if (File.Exists(destFile))
{
var seq = 1;
var baseName = Path.GetFileNameWithoutExtension(fileName);
var ext = Path.GetExtension(fileName);
do
{
destFile = Path.Combine(archiveDir, $"{baseName}_{seq}{ext}");
seq++;
} while (File.Exists(destFile));
}
// 复制到日期目录不是移动因为log4net还持有文件句柄
File.Copy(sourceFile, destFile);
// 截断当前日志文件清空内容但不删除文件保持log4net文件句柄有效
File.WriteAllText(sourceFile, string.Empty);
_log.Info($"已归档 {fileName} → {archiveDir}{fileInfo.Length} 字节)");
}
/// <summary>
/// 清理超过保留天数的日期目录
/// </summary>
private void CleanupOldDirectories()
{
if (!Directory.Exists(_logsDir))
{
return;
}
var cutoff = DateTime.Now.AddDays(-_retainDays).ToString("yyyy-MM-dd");
foreach (var dir in Directory.GetDirectories(_logsDir))
{
var dirName = Path.GetFileName(dir);
// 日期目录格式yyyy-MM-dd
if (dirName.Length == 10 && dirName.Contains("-") && dirName.CompareTo(cutoff) < 0)
{
try
{
Directory.Delete(dir, true);
_log.Info($"已清理过期日志目录:{dirName}");
}
catch (Exception ex)
{
_log.Warn($"清理过期日志目录失败:{dirName}", ex);
}
}
}
}
}
}