diff --git a/src/CncCollector/Core/CollectorEngine.cs b/src/CncCollector/Core/CollectorEngine.cs index fbf3027..d936fd2 100644 --- a/src/CncCollector/Core/CollectorEngine.cs +++ b/src/CncCollector/Core/CollectorEngine.cs @@ -30,6 +30,7 @@ namespace CncCollector.Core private Timer _dailySummaryTimer; private Timer _logCleanupTimer; private LogCleanupJob _logCleanupJob; + private LogArchiveJob _logArchiveJob; private DateTime _startTime; private long _totalSuccess; private long _totalFail; @@ -59,6 +60,8 @@ namespace CncCollector.Core _dailySummary = new DailySummaryJob(config.BusinessConnection); // 初始化分析引擎(与业务库和日志库同源,后续按需调整) _analysisEngine = new AnalysisEngine(config.BusinessConnection, config.LogConnection); + // 初始化日志归档任务(保留30天) + _logArchiveJob = new LogArchiveJob(30); } /// @@ -337,6 +340,8 @@ namespace CncCollector.Core { _lastSummaryDate = summaryDate; _dailySummary.Execute(summaryDate); + // 同步执行日志归档:把前一天的日志移到日期子目录 + _logArchiveJob.Execute(); } } } diff --git a/src/CncCollector/Jobs/LogArchiveJob.cs b/src/CncCollector/Jobs/LogArchiveJob.cs new file mode 100644 index 0000000..3ed10e0 --- /dev/null +++ b/src/CncCollector/Jobs/LogArchiveJob.cs @@ -0,0 +1,151 @@ +using System; +using System.IO; +using log4net; + +namespace CncCollector.Jobs +{ + /// + /// 日志文件归档任务。 + /// 每天凌晨将前一天的日志文件移动到按日期命名的子目录中。 + /// 目录结构示例: + /// 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 + /// 保留天数外的旧目录自动清理。 + /// + public class LogArchiveJob + { + private static readonly ILog _log = LogManager.GetLogger(typeof(LogArchiveJob)); + + /// + /// 日志保留天数 + /// + private readonly int _retainDays; + + /// + /// 日志根目录(相对于BaseDirectory) + /// + private readonly string _logsDir; + + public LogArchiveJob(int retainDays = 30) + { + _retainDays = retainDays; + _logsDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs"); + } + + /// + /// 执行归档:把当前日志文件复制到昨天的日期目录,然后截断当前文件。 + /// 应在每天凌晨00:00左右调用。 + /// + 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); + } + } + + /// + /// 归档单个日志文件到日期目录 + /// + 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} 字节)"); + } + + /// + /// 清理超过保留天数的日期目录 + /// + 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); + } + } + } + } + } +} diff --git a/src/CncCollector/log4net.config b/src/CncCollector/log4net.config index cc5a66e..93e4afa 100644 --- a/src/CncCollector/log4net.config +++ b/src/CncCollector/log4net.config @@ -11,10 +11,9 @@ - - - - + + + @@ -25,10 +24,9 @@ - - - - + + +