using System; using System.Collections.Concurrent; using System.IO; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; using System.Linq; using Microsoft.Extensions.Configuration; using System.Collections.Generic; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Serilog; using Serilog.Events; using Serilog.Extensions.Logging; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace JiShe.CollectBus.PluginFileWatcher { // 删除配置类的定义 class Program { // 配置对象,从appsettings.json加载 private static FileMonitorConfig _config = new FileMonitorConfig(); // 全局计数器和动态配置参数 private static long _totalEventsProcessed = 0; // 监控器健康状态追踪 private static DateTime _lastEventTime = DateTime.UtcNow; private static int _watcherRestartAttempts = 0; private static FileSystemWatcher _activeWatcher = null; // 事件存储和回放 private static EventStorage _eventStorage = null; private static EventReplaySession _currentReplaySession = null; private static ILogger _logger = NullLogger.Instance; static async Task Main(string[] args) { Console.OutputEncoding = System.Text.Encoding.UTF8; // 检查是否以数据库工具模式运行 if (args.Length > 0 && args[0].Equals("db", StringComparison.OrdinalIgnoreCase)) { // 运行数据库工具 await RunDatabaseUtilityAsync(args.Skip(1).ToArray()); return; } // 加载配置文件 LoadConfiguration(); // 配置Serilog ConfigureSerilog(); // 初始化日志记录器 _logger = new SerilogLoggerFactory().CreateLogger("FileMonitor"); Log.Information("高性能文件监控程序启动中..."); // 初始化事件存储 if (_config.EventStorage.EnableEventStorage) { _eventStorage = new EventStorage(_config, _logger); Log.Information("事件存储已启用,存储目录:{Directory}", _config.EventStorage.StorageDirectory); } // 打印配置信息 LogConfiguration(); // 获取监控路径:优先命令行参数,其次配置文件,最后使用当前目录 string pathToMonitor = args.Length > 0 ? args[0] : !string.IsNullOrEmpty(_config.General.DefaultMonitorPath) ? _config.General.DefaultMonitorPath : Directory.GetCurrentDirectory(); // 命令行参数可以覆盖配置文件 if (args.Length > 1 && args[1].Equals("--no-filter", StringComparison.OrdinalIgnoreCase)) { _config.General.EnableFileFiltering = false; Log.Information("通过命令行参数禁用文件类型过滤,将监控所有文件"); } else if (_config.General.EnableFileFiltering) { Log.Information("已启用文件类型过滤,仅监控以下类型: {Extensions}", string.Join(", ", _config.FileFilters.AllowedExtensions)); } if (!Directory.Exists(pathToMonitor)) { Log.Warning("错误:监控目录 '{Path}' 不存在。", pathToMonitor); Log.Information("尝试创建该目录..."); try { Directory.CreateDirectory(pathToMonitor); Log.Information("已成功创建目录: {Path}", pathToMonitor); } catch (Exception ex) { Log.Error(ex, "创建目录失败: {Message}", ex.Message); Log.Information("将使用当前目录作为监控路径。"); pathToMonitor = Directory.GetCurrentDirectory(); } } Log.Information("开始监控目录: {Path}", pathToMonitor); Log.Information("按 'Q' 退出程序,按 'R' 重新加载配置,按 'H' 检查监控器健康状态。"); if (_config.EventStorage.EnableEventStorage && _config.EventStorage.EnableEventReplay) { Log.Information("按 'P' 开始/暂停回放,按 'S' 停止回放,按 'B' 查询事件。"); } // 创建一个容量由配置文件指定的有界通道 var channel = Channel.CreateBounded(new BoundedChannelOptions(_config.Performance.ChannelCapacity) { FullMode = BoundedChannelFullMode.DropOldest // 当通道满时丢弃旧事件 }); using var cts = new CancellationTokenSource(); // 启动内存监控 var memoryMonitorTask = StartMemoryMonitorAsync(cts.Token); // 启动文件监控器 var monitorTask = StartFileMonitorAsync(pathToMonitor, channel.Writer, cts.Token); // 启动健康监控任务 var healthMonitorTask = _config.Robustness.EnableAutoRecovery ? StartWatcherHealthMonitorAsync(pathToMonitor, channel.Writer, cts.Token) : Task.CompletedTask; // 启动事件处理器 var processorTask = ProcessEventsAsync(channel.Reader, cts.Token); // 等待用户按下键退出或重新加载配置 bool needRestart = false; while (true) { var key = Console.ReadKey(true).Key; if (key == ConsoleKey.Q) { break; // 退出循环 } else if (key == ConsoleKey.R) { Log.Information("正在重新加载配置..."); LoadConfiguration(); // 重新配置Serilog ConfigureSerilog(); LogConfiguration(); Log.Information("配置已重新加载,部分配置需要重启程序才能生效。"); } else if (key == ConsoleKey.H) { // 手动检查监控器健康状态 bool isHealthy = _activeWatcher != null && FileWatcherUtils.IsWatcherHealthy(_activeWatcher, _lastEventTime, _config.Robustness); Log.Information("监控器健康状态: {Status}", (isHealthy ? "正常" : "异常")); Log.Information("上次事件时间: {Time}", _lastEventTime); Log.Information("重启次数: {Count}/{MaxCount}", _watcherRestartAttempts, _config.Robustness.MaxRestartAttempts); Log.Information("已处理事件总数: {Count}", _totalEventsProcessed); } else if (key == ConsoleKey.P && _config.EventStorage.EnableEventStorage && _config.EventStorage.EnableEventReplay) { await HandleEventReplayToggleAsync(); } else if (key == ConsoleKey.S && _config.EventStorage.EnableEventStorage && _config.EventStorage.EnableEventReplay) { await StopReplayAsync(); } else if (key == ConsoleKey.B && _config.EventStorage.EnableEventStorage) { await QueryEventsAsync(); } await Task.Delay(100); } // 取消所有任务 cts.Cancel(); // 停止回放(如果正在进行) await StopReplayAsync(); // 释放事件存储 _eventStorage?.Dispose(); try { var tasks = new List { monitorTask, processorTask, memoryMonitorTask }; if (_config.Robustness.EnableAutoRecovery) { tasks.Add(healthMonitorTask); } await Task.WhenAll(tasks); } catch (OperationCanceledException) { Log.Information("程序已正常退出。"); } // 关闭Serilog Log.CloseAndFlush(); } // 配置Serilog private static void ConfigureSerilog() { var configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .Build(); var loggerConfig = new LoggerConfiguration() .ReadFrom.Configuration(configuration); // 根据配置设置最小日志级别 LogEventLevel minimumLevel = LogEventLevel.Information; if (!string.IsNullOrEmpty(_config.Logging?.LogLevel)) { switch (_config.Logging.LogLevel.ToLower()) { case "verbose": minimumLevel = LogEventLevel.Verbose; break; case "debug": minimumLevel = LogEventLevel.Debug; break; case "information": minimumLevel = LogEventLevel.Information; break; case "warning": minimumLevel = LogEventLevel.Warning; break; case "error": minimumLevel = LogEventLevel.Error; break; case "fatal": minimumLevel = LogEventLevel.Fatal; break; } } // 如果配置文件中没有Serilog节,使用默认配置 var hasConfig = configuration.GetSection("Serilog").Exists(); if (!hasConfig) { loggerConfig .MinimumLevel.Is(minimumLevel) .WriteTo.Console( outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] [{Level:u3}] {Message:lj}{NewLine}{Exception}" ) .WriteTo.File( Path.Combine(_config.Logging?.LogDirectory ?? "Logs", "filemonitor-.log"), rollingInterval: RollingInterval.Day, outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] [{Level:u3}] {Message:lj}{NewLine}{Exception}", retainedFileCountLimit: _config.Logging?.RetainedLogDays ?? 30 ); } Log.Logger = loggerConfig.CreateLogger(); } // 查询事件 private static async Task QueryEventsAsync() { if (_eventStorage == null) { Log.Warning("事件存储未启用"); return; } try { Log.Information("\n================ 事件查询 ================"); Console.WriteLine("请输入查询参数,直接回车使用默认值"); // 收集查询参数 var queryParams = new EventQueryParams(); Console.Write("开始时间 (yyyy-MM-dd HH:mm:ss),默认24小时前: "); string startTimeStr = Console.ReadLine(); if (!string.IsNullOrEmpty(startTimeStr)) { if (DateTime.TryParse(startTimeStr, out var startTime)) { queryParams.StartTime = startTime; } else { Log.Warning("无效的日期格式,使用默认值"); queryParams.StartTime = DateTime.Now.AddDays(-1); } } else { queryParams.StartTime = DateTime.Now.AddDays(-1); } Console.Write("结束时间 (yyyy-MM-dd HH:mm:ss),默认当前时间: "); string endTimeStr = Console.ReadLine(); if (!string.IsNullOrEmpty(endTimeStr)) { if (DateTime.TryParse(endTimeStr, out var endTime)) { queryParams.EndTime = endTime; } else { Log.Warning("无效的日期格式,使用默认值"); } } Console.Write("事件类型 (Created, Modified, Deleted, Renamed, All),默认All: "); string eventTypeStr = Console.ReadLine(); if (!string.IsNullOrEmpty(eventTypeStr) && eventTypeStr.ToLower() != "all") { if (Enum.TryParse(eventTypeStr, true, out var eventType)) { queryParams.EventType = eventType; } else { Log.Warning("无效的事件类型,使用默认值All"); } } Console.Write("路径过滤 (包含此文本的路径),默认无: "); string pathFilter = Console.ReadLine(); if (!string.IsNullOrEmpty(pathFilter)) { queryParams.PathFilter = pathFilter; } Console.Write("文件扩展名过滤 (.txt, .exe等),默认无: "); string extensionFilter = Console.ReadLine(); if (!string.IsNullOrEmpty(extensionFilter)) { queryParams.ExtensionFilter = extensionFilter; } Console.Write("每页显示数量,默认20: "); string pageSizeStr = Console.ReadLine(); if (!string.IsNullOrEmpty(pageSizeStr) && int.TryParse(pageSizeStr, out var pageSize)) { queryParams.PageSize = pageSize; } else { queryParams.PageSize = 20; } // 执行查询 var result = await _eventStorage.QueryEventsAsync(queryParams); Log.Information("查询结果 (共{Count}条记录):", result.TotalCount); Log.Information("时间范围: {StartTime} 至 {EndTime}", result.StartTime, result.EndTime); Console.WriteLine("---------------------------------------------"); if (result.Events.Count == 0) { Log.Warning("未找到符合条件的事件"); } else { foreach (var evt in result.Events) { string eventType = evt.EventType.ToString(); string timeStr = evt.Timestamp.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss.fff"); Console.WriteLine($"[{timeStr}] [{eventType}] {evt.FileName}"); Console.WriteLine($" 路径: {evt.Directory}"); if (evt.EventType == FileEventType.Renamed && !string.IsNullOrEmpty(evt.OldFileName)) { Console.WriteLine($" 原名称: {evt.OldFileName}"); } Console.WriteLine(); } if (result.HasMore) { Log.Warning("... 还有更多记录未显示 ..."); } } // 询问是否开始回放 if (result.Events.Count > 0 && _config.EventStorage.EnableEventReplay) { Console.Write("\n是否要回放这些事件? (Y/N): "); string replayAnswer = Console.ReadLine(); if (!string.IsNullOrEmpty(replayAnswer) && replayAnswer.ToUpper().StartsWith("Y")) { await StartReplayAsync(queryParams); } } } catch (Exception ex) { Log.Error(ex, "查询事件时出错: {Message}", ex.Message); } Console.WriteLine("=============================================\n"); } // 处理回放切换(开始/暂停) private static async Task HandleEventReplayToggleAsync() { if (_eventStorage == null) return; try { if (_currentReplaySession == null) { // 如果没有活动的回放会话,创建一个简单的查询参数并开始回放 var queryParams = new EventQueryParams { StartTime = DateTime.Now.AddHours(-1), PageSize = 1000 }; await StartReplayAsync(queryParams); } else if (_currentReplaySession.IsPaused) { // 恢复回放 await _currentReplaySession.ResumeAsync(); Log.Information("已恢复事件回放"); } else { // 暂停回放 await _currentReplaySession.PauseAsync(); Log.Information("已暂停事件回放"); } } catch (Exception ex) { Log.Error(ex, "处理回放时出错: {Message}", ex.Message); } } // 开始回放 private static async Task StartReplayAsync(EventQueryParams queryParams) { if (_eventStorage == null || !_config.EventStorage.EnableEventReplay) return; try { // 停止现有回放 await StopReplayAsync(); // 创建回放处理器 Func replayHandler = async (e) => { // 在控制台上显示回放的事件 string eventType = e.EventType.ToString(); string timeStr = e.Timestamp.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss.fff"); Log.Information("[回放] [{Time}] [{Type}] {FileName}", timeStr, eventType, e.FileName); if (_config.Logging.LogFileEventDetails) { Log.Debug(" 路径: {Path}", e.Directory); if (e.EventType == FileEventType.Renamed && !string.IsNullOrEmpty(e.OldFileName)) { Log.Debug(" 原名称: {OldName}", e.OldFileName); } } // 这里可以添加实际的文件操作,比如创建、删除文件等 // 但在此示例中,我们只是显示事件而不执行实际操作 await Task.CompletedTask; }; // 开始回放会话 _currentReplaySession = await _eventStorage.StartReplayAsync( queryParams, replayHandler, CancellationToken.None); // 如果会话创建成功,开始回放 if (_currentReplaySession != null) { await _currentReplaySession.StartAsync(); Log.Information("开始回放事件,共 {Count} 个事件", _currentReplaySession.TotalEvents); Log.Information("回放速度: {Speed}", (_config.EventStorage.ReplaySpeedFactor > 1 ? $"{_config.EventStorage.ReplaySpeedFactor}x (加速)" : $"{_config.EventStorage.ReplaySpeedFactor}x (减速)")); } else { Log.Warning("未找到符合条件的事件,回放未开始"); } } catch (Exception ex) { Log.Error(ex, "开始回放时出错: {Message}", ex.Message); } } // 停止回放 private static async Task StopReplayAsync() { if (_currentReplaySession != null) { try { await _currentReplaySession.StopAsync(); Log.Information("已停止事件回放"); // 输出回放统计 Log.Information("回放统计: 共处理 {Processed}/{Total} 个事件", _currentReplaySession.ProcessedEvents, _currentReplaySession.TotalEvents); TimeSpan duration = (_currentReplaySession.EndTime ?? DateTime.Now) - _currentReplaySession.StartTime; Log.Information("回放持续时间: {Duration:F1} 秒", duration.TotalSeconds); } catch (Exception ex) { Log.Error(ex, "停止回放时出错: {Message}", ex.Message); } finally { _currentReplaySession.Dispose(); _currentReplaySession = null; } } } // 加载配置文件 private static void LoadConfiguration() { try { // 检查配置文件是否存在 string configPath = Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json"); if (!File.Exists(configPath)) { Log.Warning("配置文件不存在,使用默认配置。"); // 使用默认配置 _config = new FileMonitorConfig { General = new GeneralConfig { EnableFileFiltering = true, MemoryMonitorIntervalMinutes = 1, DefaultMonitorPath = Path.Combine(Directory.GetCurrentDirectory(), "MonitorFiles") }, FileFilters = new FileFiltersConfig { AllowedExtensions = new[] { ".dll" }, ExcludedDirectories = new[] { "bin", "obj", "node_modules" }, IncludeSubdirectories = true }, Performance = new PerformanceConfig { MemoryCleanupThreshold = 5000, ChannelCapacity = 1000, EventDebounceTimeSeconds = 3, MaxDictionarySize = 10000, CleanupIntervalSeconds = 5, ProcessingDelayMs = 5 }, Robustness = new RobustnessConfig { EnableAutoRecovery = true, WatcherHealthCheckIntervalSeconds = 30, WatcherTimeoutSeconds = 60, MaxRestartAttempts = 3, RestartDelaySeconds = 5, EnableFileLockDetection = true, LockedFileStrategy = "Retry", FileLockRetryCount = 3, FileLockRetryDelayMs = 500 }, NotifyFilters = new List { "LastWrite", "FileName", "DirectoryName", "CreationTime" }, EventStorage = new EventStorageConfig { EnableEventStorage = false, StorageDirectory = Path.Combine(Directory.GetCurrentDirectory(), "EventStorage"), EnableEventReplay = false, ReplaySpeedFactor = 1 }, Logging = new LoggingConfig { LogLevel = "Information", LogFileEventDetails = false, RetainedLogDays = 30, LogDirectory = "Logs" } }; return; } var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); var configuration = builder.Build(); // 绑定配置到对象 var config = new FileMonitorConfig(); configuration.GetSection("FileMonitorConfig").Bind(config); // 更新全局配置 _config = config; } catch (Exception ex) { Log.Error(ex, "加载配置文件失败: {Message}", ex.Message); Log.Warning("将使用默认配置。"); // 使用默认配置 _config = new FileMonitorConfig { General = new GeneralConfig { EnableFileFiltering = true, MemoryMonitorIntervalMinutes = 1, DefaultMonitorPath = Path.Combine(Directory.GetCurrentDirectory(), "MonitorFiles") }, FileFilters = new FileFiltersConfig { AllowedExtensions = new[] { ".dll" }, ExcludedDirectories = new[] { "bin", "obj", "node_modules" }, IncludeSubdirectories = true }, Performance = new PerformanceConfig { MemoryCleanupThreshold = 5000, ChannelCapacity = 1000, EventDebounceTimeSeconds = 3, MaxDictionarySize = 10000, CleanupIntervalSeconds = 5, ProcessingDelayMs = 5 }, Robustness = new RobustnessConfig { EnableAutoRecovery = true, WatcherHealthCheckIntervalSeconds = 30, WatcherTimeoutSeconds = 60, MaxRestartAttempts = 3, RestartDelaySeconds = 5, EnableFileLockDetection = true, LockedFileStrategy = "Retry", FileLockRetryCount = 3, FileLockRetryDelayMs = 500 }, NotifyFilters = new List { "LastWrite", "FileName", "DirectoryName", "CreationTime" }, EventStorage = new EventStorageConfig { EnableEventStorage = false, StorageDirectory = Path.Combine(Directory.GetCurrentDirectory(), "EventStorage"), EnableEventReplay = false, ReplaySpeedFactor = 1 }, Logging = new LoggingConfig { LogLevel = "Information", LogFileEventDetails = false, RetainedLogDays = 30, LogDirectory = "Logs" } }; } } // 打印当前配置信息 private static void LogConfiguration() { Log.Information("当前配置信息:"); Log.Information(" 默认监控路径: {Path}", _config.General.DefaultMonitorPath); Log.Information(" 文件过滤: {Status}", (_config.General.EnableFileFiltering ? "启用" : "禁用")); if (_config.General.EnableFileFiltering) { Log.Information(" 监控文件类型: {Types}", string.Join(", ", _config.FileFilters.AllowedExtensions)); Log.Information(" 排除目录: {Dirs}", string.Join(", ", _config.FileFilters.ExcludedDirectories)); } Log.Information(" 内存监控间隔: {Minutes}分钟", _config.General.MemoryMonitorIntervalMinutes); Log.Information(" 内存清理阈值: 每{Threshold}个事件", _config.Performance.MemoryCleanupThreshold); Log.Information(" 通道容量: {Capacity}", _config.Performance.ChannelCapacity); Log.Information(" 事件去抖时间: {Seconds}秒", _config.Performance.EventDebounceTimeSeconds); Log.Information(" 自动恢复: {Status}", (_config.Robustness.EnableAutoRecovery ? "启用" : "禁用")); Log.Information(" 文件锁检测: {Status}", (_config.Robustness.EnableFileLockDetection ? "启用" : "禁用")); if (_config.Robustness.EnableFileLockDetection) { Log.Information(" 锁定文件策略: {Strategy}", _config.Robustness.LockedFileStrategy); } Log.Information(" 日志级别: {Level}", _config.Logging?.LogLevel ?? "Information"); } // 检查文件是否应该被处理的方法 private static bool ShouldProcessFile(string filePath) { if (!_config.General.EnableFileFiltering) return true; if (string.IsNullOrEmpty(filePath)) return false; // 检查是否在排除目录中 foreach (var excludedDir in _config.FileFilters.ExcludedDirectories) { if (filePath.Contains($"{Path.DirectorySeparatorChar}{excludedDir}{Path.DirectorySeparatorChar}", StringComparison.OrdinalIgnoreCase) || filePath.EndsWith($"{Path.DirectorySeparatorChar}{excludedDir}", StringComparison.OrdinalIgnoreCase)) { return false; } } string extension = Path.GetExtension(filePath); return _config.FileFilters.AllowedExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase); } // 添加内存监控方法 private static async Task StartMemoryMonitorAsync(CancellationToken cancellationToken) { try { while (!cancellationToken.IsCancellationRequested) { // 等待指定时间 await Task.Delay(TimeSpan.FromMinutes(_config.General.MemoryMonitorIntervalMinutes), cancellationToken); // 获取当前内存使用情况 long currentMemory = GC.GetTotalMemory(false) / (1024 * 1024); // MB Log.Information("内存使用: {Memory} MB, 已处理事件: {EventCount}", currentMemory, _totalEventsProcessed); // 强制执行垃圾回收 GC.Collect(); GC.WaitForPendingFinalizers(); // 显示垃圾回收后的内存 long afterGCMemory = GC.GetTotalMemory(true) / (1024 * 1024); // MB Log.Debug("内存清理后: {Memory} MB (释放: {Released} MB)", afterGCMemory, currentMemory - afterGCMemory); } } catch (OperationCanceledException) { // 预期的取消异常 } } // 创建文件监控器 private static FileSystemWatcher CreateFileWatcher(string path, ChannelWriter writer, CancellationToken cancellationToken) { // 创建一个新的文件系统监控器 var watcher = new FileSystemWatcher(path); // 从配置设置NotifyFilters NotifyFilters notifyFilters = NotifyFilters.LastWrite; // 默认值 foreach (var filterName in _config.NotifyFilters) { if (Enum.TryParse(filterName, out var filter)) { notifyFilters |= filter; } } watcher.NotifyFilter = notifyFilters; watcher.IncludeSubdirectories = _config.FileFilters.IncludeSubdirectories; watcher.EnableRaisingEvents = true; // 设置文件过滤器,如果启用了过滤 if (_config.General.EnableFileFiltering && _config.FileFilters.AllowedExtensions.Length > 0) { // 在FileSystemWatcher级别设置过滤器,这样可以减少系统生成的事件数量 // 只能设置一个扩展名作为Filter,所以我们选择第一个 string firstExtension = _config.FileFilters.AllowedExtensions[0]; watcher.Filter = $"*{firstExtension}"; Log.Information("已设置文件系统监控过滤器: *{Filter}", firstExtension); if (_config.FileFilters.AllowedExtensions.Length > 1) { Log.Warning("注意: FileSystemWatcher只支持单一过滤器,其他文件类型将在事件处理时过滤。"); } } // 注册事件处理程序 watcher.Created += (sender, e) => HandleFileEvent(sender, e, writer, cancellationToken); watcher.Changed += (sender, e) => HandleFileEvent(sender, e, writer, cancellationToken); watcher.Deleted += (sender, e) => HandleFileEvent(sender, e, writer, cancellationToken); watcher.Renamed += (sender, e) => HandleFileEvent(sender, e, writer, cancellationToken); watcher.Error += (sender, e) => { Log.Error(e.GetException(), "文件监控错误: {Message}", e.GetException().Message); }; return watcher; } // 文件事件处理 private static void HandleFileEvent(object sender, FileSystemEventArgs e, ChannelWriter writer, CancellationToken cancellationToken) { // 更新最后事件时间 _lastEventTime = DateTime.UtcNow; // 如果启用了事件存储,记录事件 _eventStorage?.RecordEvent(e); try { // 如果是退出信号,忽略后续处理 if (cancellationToken.IsCancellationRequested) { return; } // 只处理指定类型的文件,如果启用了文件过滤 if (!ShouldProcessFile(e.FullPath)) { return; } // 尝试将事件写入通道,如果满了就丢弃 // 不等待,以免阻塞文件系统事件 if (!writer.TryWrite(e)) { Log.Warning("警告: 事件处理队列已满,部分事件被丢弃"); } } catch (Exception ex) { // 捕获任何异常,防止崩溃 Log.Error(ex, "处理文件事件时出错: {Message}", ex.Message); } } private static async Task StartFileMonitorAsync(string path, ChannelWriter writer, CancellationToken cancellationToken) { // 使用TaskCompletionSource来控制任务的完成 var tcs = new TaskCompletionSource(); try { // 创建并启动监控器 _activeWatcher = CreateFileWatcher(path, writer, cancellationToken); // 注册取消回调 cancellationToken.Register(() => { try { if (_activeWatcher != null) { // 确保所有资源正确清理 _activeWatcher.EnableRaisingEvents = false; _activeWatcher.Dispose(); } } catch (Exception ex) { Log.Error(ex, "关闭文件监控器时出错: {Message}", ex.Message); } finally { tcs.TrySetResult(true); } }); // 等待任务被取消或完成 await tcs.Task; } catch (Exception ex) { Log.Error(ex, "启动文件监控器时出错: {Message}", ex.Message); tcs.TrySetException(ex); throw; } } private static async Task ProcessEventsAsync(ChannelReader reader, CancellationToken cancellationToken) { try { // 使用配置的字典大小 var recentlyProcessed = new ConcurrentDictionary(); // 清理计时器,频率由配置决定 using var cleanupTimer = new Timer(_ => { try { var cutoff = DateTime.UtcNow.AddSeconds(-_config.Performance.EventDebounceTimeSeconds); int removedCount = 0; // 限制字典大小 if (recentlyProcessed.Count > _config.Performance.MaxDictionarySize) { // 找出最旧的条目删除 var oldestItems = recentlyProcessed .OrderBy(kv => kv.Value) .Take(recentlyProcessed.Count - _config.Performance.MaxDictionarySize / 2); foreach (var item in oldestItems) { DateTime dummy; if (recentlyProcessed.TryRemove(item.Key, out dummy)) { removedCount++; } } } // 常规清理 foreach (var key in recentlyProcessed.Keys) { if (recentlyProcessed.TryGetValue(key, out var time) && time < cutoff) { DateTime dummy; if (recentlyProcessed.TryRemove(key, out dummy)) { removedCount++; } } } if (removedCount > 0) { Log.Debug("已清理 {Count} 个过期事件记录", removedCount); } } catch (Exception ex) { Log.Error(ex, "清理定时器错误: {Message}", ex.Message); } }, null, TimeSpan.FromSeconds(_config.Performance.CleanupIntervalSeconds), TimeSpan.FromSeconds(_config.Performance.CleanupIntervalSeconds)); // 从通道读取事件并处理 await foreach (var e in reader.ReadAllAsync(cancellationToken)) { // 再次确认文件扩展名,双重检查 if (!ShouldProcessFile(e.FullPath)) { continue; } // 增加计数器 long count = Interlocked.Increment(ref _totalEventsProcessed); // 周期性清理内存 if (count % _config.Performance.MemoryCleanupThreshold == 0) { // 在后台线程执行垃圾回收,但直接等待完成避免警告 await Task.Run(() => { GC.Collect(); GC.WaitForPendingFinalizers(); }); } // 只使用文件路径作为键,忽略事件类型,这样同一文件的创建和修改事件会被视为同一事件 var key = e.FullPath; // 对于创建事件,如果之前有相同文件的其他事件,则跳过 if (e.ChangeType == WatcherChangeTypes.Changed && recentlyProcessed.TryGetValue(key, out var _)) { // 如果是修改事件且文件最近被处理过,则跳过 continue; } // 更新或添加文件处理时间,使用原子操作 recentlyProcessed[key] = DateTime.UtcNow; string eventType = e.ChangeType switch { WatcherChangeTypes.Created => "创建", WatcherChangeTypes.Deleted => "删除", WatcherChangeTypes.Changed => "修改", WatcherChangeTypes.Renamed => "重命名", _ => "未知" }; string fileName = e.Name ?? Path.GetFileName(e.FullPath); string directoryName = Path.GetDirectoryName(e.FullPath) ?? string.Empty; Log.Information("{EventType}: {FileName}", eventType, fileName); if (_config.Logging.LogFileEventDetails) { Log.Debug(" 路径: {Path}", directoryName); var extension = Path.GetExtension(e.FullPath); Log.Debug(" 类型: {Extension}", extension); if (e is RenamedEventArgs renamedEvent) { Log.Debug(" 原名称: {OldName}", Path.GetFileName(renamedEvent.OldFullPath)); } } // 延迟可配置 await Task.Delay(_config.Performance.ProcessingDelayMs, cancellationToken); } } catch (OperationCanceledException) { // 预期的取消异常,正常退出 } catch (Exception ex) { Log.Error(ex, "处理事件时发生错误: {Message}", ex.Message); } finally { // 确保资源释放 GC.Collect(); GC.WaitForPendingFinalizers(); } } // 健康监控任务,监视文件监控器的状态并在需要时重启它 private static async Task StartWatcherHealthMonitorAsync( string path, ChannelWriter writer, CancellationToken cancellationToken) { Log.Information("已启动监控器健康检查,间隔: {Seconds}秒", _config.Robustness.WatcherHealthCheckIntervalSeconds); try { while (!cancellationToken.IsCancellationRequested) { // 等待指定的健康检查间隔 await Task.Delay(TimeSpan.FromSeconds(_config.Robustness.WatcherHealthCheckIntervalSeconds), cancellationToken); // 如果监控器不健康且重启次数未超过上限,尝试重启 if (_activeWatcher != null && !FileWatcherUtils.IsWatcherHealthy(_activeWatcher, _lastEventTime, _config.Robustness) && _watcherRestartAttempts < _config.Robustness.MaxRestartAttempts) { // 停止当前的监控器 Log.Warning("检测到监控器异常,准备重启..."); try { _activeWatcher.EnableRaisingEvents = false; _activeWatcher.Dispose(); } catch (Exception ex) { Log.Error(ex, "停止异常监控器时出错: {Message}", ex.Message); } _watcherRestartAttempts++; Log.Warning("正在重启监控器,尝试次数: {Current}/{Max}", _watcherRestartAttempts, _config.Robustness.MaxRestartAttempts); // 等待指定的重启延迟时间 await Task.Delay(TimeSpan.FromSeconds(_config.Robustness.RestartDelaySeconds), cancellationToken); // 创建新的监控器 try { _activeWatcher = CreateFileWatcher(path, writer, cancellationToken); _lastEventTime = DateTime.UtcNow; // 重置最后事件时间 Log.Information("监控器已成功重启"); } catch (Exception ex) { Log.Error(ex, "重启监控器失败: {Message}", ex.Message); } } else if (_watcherRestartAttempts >= _config.Robustness.MaxRestartAttempts) { Log.Error("警告: 已达到最大重启次数({Max}),不再尝试重启", _config.Robustness.MaxRestartAttempts); Log.Warning("请检查文件系统或手动重启程序"); break; } } } catch (OperationCanceledException) { // 预期的取消异常,正常退出 } catch (Exception ex) { Log.Error(ex, "健康监控任务异常: {Message}", ex.Message); } } /// /// 运行数据库工具 /// private static async Task RunDatabaseUtilityAsync(string[] args) { Console.WriteLine("正在启动SQLite数据库管理工具..."); try { var dbUtility = new DbUtility(); await dbUtility.ExecuteAsync(args); } catch (Exception ex) { Console.WriteLine($"运行数据库工具时发生错误: {ex.Message}"); } } } }