using OfficeOpenXml.FormulaParsing.Excel.Functions.Numeric; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using WIDESEAWCS_Core.Helper; namespace WIDESEAWCS_Core.LogHelper { /// /// 日志等级枚举 /// public enum LogLevel { DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3, FATAL = 4 } /// /// 日志条目:封装单条日志的完整信息 /// public class LogEntry { /// /// 日志时间 /// public DateTime Time { get; set; } /// /// 日志等级 /// public LogLevel Level { get; set; } /// /// 日志消息内容 /// public string Message { get; set; } = string.Empty; /// /// 日志来源/文件名 /// public string? Source { get; set; } /// /// 异常信息(可选) /// public string? Exception { get; set; } /// /// 格式化为标准日志字符串 /// public string ToFormattedString() { var sb = new StringBuilder(); sb.Append($"【{Time:HH:mm:ss}】【{Level}】:【{Message}】"); if (!string.IsNullOrEmpty(Exception)) { sb.AppendLine(); sb.Append($"【异常】:{Exception}"); } return sb.ToString(); } } /// /// 队列化集中日志写入器 /// 使用生产者-消费者模式,后台线程批量写入文件,提高性能 /// public class QueuedLogWriter : IDisposable { private static readonly Lazy _instance = new Lazy(() => new QueuedLogWriter()); public static QueuedLogWriter Instance => _instance.Value; private readonly BlockingCollection _logQueue; private readonly CancellationTokenSource _cts; private readonly Task _writeTask; private readonly ReaderWriterLockSlim _fileLock = new ReaderWriterLockSlim(); private readonly string _logFolder; private readonly int _maxFileSize; private readonly string _fileExt; private bool _disposed; /// /// 日志队列写入器单例 /// private QueuedLogWriter() { _logFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"Log{Path.DirectorySeparatorChar}{DateTime.Now:yyyy-MM-dd}"); _maxFileSize = 10 * 1024 * 1024; // 10MB _fileExt = ".log"; _logQueue = new BlockingCollection(new ConcurrentQueue()); _cts = new CancellationTokenSource(); // 确保日志目录存在 if (!Directory.Exists(_logFolder)) { Directory.CreateDirectory(_logFolder); } // 启动后台写入线程 _writeTask = Task.Run(WriteLoop, _cts.Token); } /// /// 后台写入循环 /// private void WriteLoop() { var batch = new List(); while (!_cts.Token.IsCancellationRequested) { try { // 批量取出日志,最多100条或等待500ms batch.Clear(); if (_logQueue.TryTake(out var entry, 500)) { batch.Add(entry); while (batch.Count < 100 && _logQueue.TryTake(out entry, 50)) { batch.Add(entry); } WriteBatch(batch); } } catch (OperationCanceledException) { break; } catch (Exception) { // 忽略写入异常,防止后台线程崩溃 } } // 取消前将剩余日志写出 while (_logQueue.TryTake(out var remainingEntry, 100)) { batch.Add(remainingEntry); } if (batch.Count > 0) { WriteBatch(batch); } } /// /// 批量写入日志到文件 /// private void WriteBatch(List entries) { if (entries.Count == 0) return; try { _fileLock.EnterWriteLock(); foreach (var entry in entries) { string fileName = GetLogFileName(entry.Source); string filePath = Path.Combine(_logFolder, fileName); string content = entry.ToFormattedString() + Environment.NewLine; ConsoleHelper.WriteInfoLine(content); File.AppendAllText(filePath, content); } } catch (Exception) { // 静默处理,防止文件IO异常影响主线程 } finally { _fileLock.ExitWriteLock(); } } /// /// 获取或创建一个日志文件路径(按大小分文件) /// private string GetLogFileName(string? source) { string prefix = string.IsNullOrEmpty(source) ? "WCS" : source; string searchPattern = $"{prefix}*.log"; if (!Directory.Exists(_logFolder)) { Directory.CreateDirectory(_logFolder); } var files = Directory.GetFiles(_logFolder, searchPattern) .Select(f => new FileInfo(f)) .Where(f => f.Extension.Equals(_fileExt, StringComparison.OrdinalIgnoreCase)) .OrderByDescending(f => f.Name) .ToList(); // 查找有空间的现有文件 foreach (var file in files) { if (file.Length < _maxFileSize) { return file.Name; } } // 创建新文件 return $"{prefix}_{DateTime.Now:HH-mm-ss}{_fileExt}"; } /// /// 写入日志(生产端) /// public void Enqueue(LogEntry entry) { if (_disposed) return; _logQueue.Add(entry); } /// /// 写入日志的便捷方法 /// public void Log(LogLevel level, string message, string? source = null, string? exception = null) { if (_disposed) return; var entry = new LogEntry { Time = DateTime.Now, Level = level, Message = message, Source = source, Exception = exception }; _logQueue.Add(entry); } /// /// 关闭日志写入器 /// public void Dispose() { if (_disposed) return; _disposed = true; _cts.Cancel(); try { _writeTask.Wait(3000); } catch (AggregateException) { } _cts.Dispose(); _logQueue.Dispose(); _fileLock.Dispose(); } } /// /// 静态日志工具类(兼容原有接口) /// 提供多种格式化方法和队列化写入 /// public class QuartzLogger { private static readonly QueuedLogWriter _writer = QueuedLogWriter.Instance; /// /// 兼容旧接口:将日志写入文件 /// /// 日志文件名(作为Source) /// 日志内容 public static void WriteLogToFile(string fileName, string log) { _writer.Log(LogLevel.INFO, log, fileName); } /// /// 写入调试日志 /// public static void Debug(string message, string? source = null) { _writer.Log(LogLevel.DEBUG, message, source); } /// /// 写入信息日志 /// public static void Info(string message, string? source = null) { _writer.Log(LogLevel.INFO, message, source); } /// /// 写入警告日志 /// public static void Warn(string message, string? source = null) { _writer.Log(LogLevel.WARN, message, source); } /// /// 写入错误日志 /// public static void Error(string message, string? source = null, Exception? exception = null) { _writer.Log(LogLevel.ERROR, message, source, exception?.ToString()); } /// /// 写入致命错误日志 /// public static void Fatal(string message, string? source = null, Exception? exception = null) { _writer.Log(LogLevel.FATAL, message, source, exception?.ToString()); } /// /// 使用指定格式写入日志 /// /// 格式化字符串 /// 格式化参数 public static void WriteFormatted(string format, params object[] args) { string message = string.Format(format, args); _writer.Log(LogLevel.INFO, message); } /// /// 直接写入一个日志条目(最灵活) /// public static void WriteLogEntry(LogEntry entry) { _writer.Enqueue(entry); } /// /// 释放资源(应用关闭时调用) /// public static void Shutdown() { _writer.Dispose(); } } }