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
|
{
|
/// <summary>
|
/// 日志等级枚举
|
/// </summary>
|
public enum LogLevel
|
{
|
DEBUG = 0,
|
INFO = 1,
|
WARN = 2,
|
ERROR = 3,
|
FATAL = 4
|
}
|
|
/// <summary>
|
/// 日志条目:封装单条日志的完整信息
|
/// </summary>
|
public class LogEntry
|
{
|
/// <summary>
|
/// 日志时间
|
/// </summary>
|
public DateTime Time { get; set; }
|
|
/// <summary>
|
/// 日志等级
|
/// </summary>
|
public LogLevel Level { get; set; }
|
|
/// <summary>
|
/// 日志消息内容
|
/// </summary>
|
public string Message { get; set; } = string.Empty;
|
|
/// <summary>
|
/// 日志来源/文件名
|
/// </summary>
|
public string? Source { get; set; }
|
|
/// <summary>
|
/// 异常信息(可选)
|
/// </summary>
|
public string? Exception { get; set; }
|
|
/// <summary>
|
/// 格式化为标准日志字符串
|
/// </summary>
|
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();
|
}
|
}
|
|
/// <summary>
|
/// 队列化集中日志写入器
|
/// 使用生产者-消费者模式,后台线程批量写入文件,提高性能
|
/// </summary>
|
public class QueuedLogWriter : IDisposable
|
{
|
private static readonly Lazy<QueuedLogWriter> _instance = new Lazy<QueuedLogWriter>(() => new QueuedLogWriter());
|
public static QueuedLogWriter Instance => _instance.Value;
|
|
private readonly BlockingCollection<LogEntry> _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;
|
|
/// <summary>
|
/// 日志队列写入器单例
|
/// </summary>
|
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<LogEntry>(new ConcurrentQueue<LogEntry>());
|
_cts = new CancellationTokenSource();
|
|
// 确保日志目录存在
|
if (!Directory.Exists(_logFolder))
|
{
|
Directory.CreateDirectory(_logFolder);
|
}
|
|
// 启动后台写入线程
|
_writeTask = Task.Run(WriteLoop, _cts.Token);
|
}
|
|
/// <summary>
|
/// 后台写入循环
|
/// </summary>
|
private void WriteLoop()
|
{
|
var batch = new List<LogEntry>();
|
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);
|
}
|
}
|
|
/// <summary>
|
/// 批量写入日志到文件
|
/// </summary>
|
private void WriteBatch(List<LogEntry> 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();
|
}
|
}
|
|
/// <summary>
|
/// 获取或创建一个日志文件路径(按大小分文件)
|
/// </summary>
|
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}";
|
}
|
|
/// <summary>
|
/// 写入日志(生产端)
|
/// </summary>
|
public void Enqueue(LogEntry entry)
|
{
|
if (_disposed) return;
|
_logQueue.Add(entry);
|
}
|
|
/// <summary>
|
/// 写入日志的便捷方法
|
/// </summary>
|
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);
|
}
|
|
/// <summary>
|
/// 关闭日志写入器
|
/// </summary>
|
public void Dispose()
|
{
|
if (_disposed) return;
|
_disposed = true;
|
_cts.Cancel();
|
try
|
{
|
_writeTask.Wait(3000);
|
}
|
catch (AggregateException) { }
|
_cts.Dispose();
|
_logQueue.Dispose();
|
_fileLock.Dispose();
|
}
|
}
|
|
/// <summary>
|
/// 静态日志工具类(兼容原有接口)
|
/// 提供多种格式化方法和队列化写入
|
/// </summary>
|
public class QuartzLogger
|
{
|
private static readonly QueuedLogWriter _writer = QueuedLogWriter.Instance;
|
|
/// <summary>
|
/// 兼容旧接口:将日志写入文件
|
/// </summary>
|
/// <param name="fileName">日志文件名(作为Source)</param>
|
/// <param name="log">日志内容</param>
|
public static void WriteLogToFile(string fileName, string log)
|
{
|
_writer.Log(LogLevel.INFO, log, fileName);
|
}
|
|
/// <summary>
|
/// 写入调试日志
|
/// </summary>
|
public static void Debug(string message, string? source = null)
|
{
|
_writer.Log(LogLevel.DEBUG, message, source);
|
}
|
|
/// <summary>
|
/// 写入信息日志
|
/// </summary>
|
public static void Info(string message, string? source = null)
|
{
|
_writer.Log(LogLevel.INFO, message, source);
|
}
|
|
/// <summary>
|
/// 写入警告日志
|
/// </summary>
|
public static void Warn(string message, string? source = null)
|
{
|
_writer.Log(LogLevel.WARN, message, source);
|
}
|
|
/// <summary>
|
/// 写入错误日志
|
/// </summary>
|
public static void Error(string message, string? source = null, Exception? exception = null)
|
{
|
_writer.Log(LogLevel.ERROR, message, source, exception?.ToString());
|
}
|
|
/// <summary>
|
/// 写入致命错误日志
|
/// </summary>
|
public static void Fatal(string message, string? source = null, Exception? exception = null)
|
{
|
_writer.Log(LogLevel.FATAL, message, source, exception?.ToString());
|
}
|
|
/// <summary>
|
/// 使用指定格式写入日志
|
/// </summary>
|
/// <param name="format">格式化字符串</param>
|
/// <param name="args">格式化参数</param>
|
public static void WriteFormatted(string format, params object[] args)
|
{
|
string message = string.Format(format, args);
|
_writer.Log(LogLevel.INFO, message);
|
}
|
|
/// <summary>
|
/// 直接写入一个日志条目(最灵活)
|
/// </summary>
|
public static void WriteLogEntry(LogEntry entry)
|
{
|
_writer.Enqueue(entry);
|
}
|
|
/// <summary>
|
/// 释放资源(应用关闭时调用)
|
/// </summary>
|
public static void Shutdown()
|
{
|
_writer.Dispose();
|
}
|
}
|
}
|