|
|
|
|
@ -0,0 +1,151 @@
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|