279 lines
10 KiB
C#
279 lines
10 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
|
||
using System.IO;
|
||
using System.Threading;
|
||
using SLC1_N;
|
||
|
||
namespace SLC1_N
|
||
{
|
||
public class mxlLog // 日志文件类
|
||
{
|
||
// 日志级别
|
||
public enum LogLevel
|
||
{
|
||
LOG_DEBUG,
|
||
LOG_INFO,
|
||
LOG_WARNING,
|
||
LOG_ERROR,
|
||
LOG_XXX
|
||
};
|
||
|
||
// 单例实例
|
||
private static readonly mxlLog instance = new mxlLog();
|
||
public static mxlLog Instance { get { return instance; } }
|
||
|
||
private readonly object mutex = new object();
|
||
private string logDir;
|
||
private string logPrefix = "log_";
|
||
private long maxFileSize = 10 * 1024 * 1024; // 10MB
|
||
private int maxBackupFiles = 5; // 允许文件最大备份数
|
||
private LogLevel minLevel = LogLevel.LOG_DEBUG; // 记录级别,大于则生成文件
|
||
|
||
// 标志位控制
|
||
public bool IsDebugEnabled { get; set; } = false;
|
||
public bool IsInfoEnabled { get; set; } = false;
|
||
public bool IsWarningEnabled { get; set; } = false;
|
||
public bool IsErrorEnabled { get; set; } = false;
|
||
public bool IsXXXEnabled { get; set; } = false;
|
||
|
||
// 构造函数 - 自动初始化到当前目录
|
||
private mxlLog()
|
||
{
|
||
// 自动设置为软件当前目录下的Logs文件夹
|
||
logDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
|
||
minLevel = LogLevel.LOG_DEBUG;
|
||
|
||
// 确保日志目录存在
|
||
if (!Directory.Exists(logDir))
|
||
{
|
||
Directory.CreateDirectory(logDir);
|
||
}
|
||
}
|
||
|
||
// 初始化
|
||
public void Initialize(string dir = null, // 修改日志目录
|
||
string prefix = null, // 修改日志文件名前缀
|
||
long? maxSize = null, // 限制单个日志文件最大
|
||
int? maxBackups = null,// 最多保留备份数
|
||
LogLevel? minLevel = null, // 只写入xxx及以上级别
|
||
bool? isDebugEnabled = null, bool? isInfoEnabled = null, // 写入使能
|
||
bool? isWarningEnabled = null, bool? isErrorEnabled = null,
|
||
bool? isXXXEnabled = null)
|
||
{
|
||
lock (mutex)
|
||
{
|
||
this.logDir = dir ?? this.logDir;
|
||
this.logPrefix = prefix ?? this.logPrefix;
|
||
this.maxFileSize = maxSize ?? this.maxFileSize;
|
||
this.maxBackupFiles = maxBackups ?? this.maxBackupFiles;
|
||
this.minLevel = minLevel ?? this.minLevel;
|
||
|
||
// 设置标志位
|
||
IsDebugEnabled = isDebugEnabled ?? IsDebugEnabled;
|
||
IsInfoEnabled = isInfoEnabled ?? IsInfoEnabled;
|
||
IsWarningEnabled = isWarningEnabled ?? IsWarningEnabled;
|
||
IsErrorEnabled = isErrorEnabled ?? IsErrorEnabled;
|
||
IsXXXEnabled = isXXXEnabled ?? IsXXXEnabled;
|
||
|
||
if (!Directory.Exists(logDir))
|
||
{
|
||
Directory.CreateDirectory(logDir);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 写入日志
|
||
public void Write(LogLevel level, string message, Exception ex = null)
|
||
{
|
||
if (!IsLevelEnabled(level) || level < minLevel) // 检查使能
|
||
return;
|
||
|
||
lock (mutex)
|
||
{
|
||
try
|
||
{
|
||
string logFile = GetCurrentLogFilePath();
|
||
CheckFileSize(logFile);
|
||
|
||
string logEntry = $"{GetCurrentTimeStamp()} [{GetLevelString(level)}] {message}";
|
||
|
||
if (ex != null)
|
||
{
|
||
logEntry += $"\nException: {ex.GetType().Name}\nMessage: {ex.Message}\nStack Trace: {ex.StackTrace}";
|
||
}
|
||
|
||
File.AppendAllText(logFile, logEntry + Environment.NewLine);
|
||
}
|
||
catch (Exception logEx)
|
||
{
|
||
HandleLogFailure(logEx);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 清理旧日志文件
|
||
public void ClearOldLogs(int keepDays = 30)
|
||
{
|
||
try
|
||
{
|
||
lock (mutex)
|
||
{
|
||
if (!Directory.Exists(logDir))
|
||
return;
|
||
|
||
var cutoffDate = DateTime.Now.AddDays(-keepDays); // 截止日期
|
||
|
||
var allLogFiles = Directory.GetFiles(logDir, $"{logPrefix}*.log*"); // 筛选
|
||
|
||
foreach (var file in allLogFiles)
|
||
{
|
||
try
|
||
{
|
||
// 尝试从文件名中解析日期
|
||
var fileName = Path.GetFileNameWithoutExtension(file);
|
||
|
||
if (fileName.StartsWith(logPrefix))
|
||
{
|
||
var datePart = fileName.Substring(logPrefix.Length); //去掉前缀,获取日期部分
|
||
|
||
// 尝试解析日期 yyyyMMdd
|
||
if (datePart.Length >= 8 && // 日期是否8个字符("20250101") 则存到fileDate
|
||
DateTime.TryParseExact(datePart.Substring(0, 8), "yyyyMMdd", null, System.Globalization.DateTimeStyles.None, out var fileDate))
|
||
{
|
||
if (fileDate < cutoffDate) // 对比日期
|
||
{
|
||
File.Delete(file);
|
||
Write(LogLevel.LOG_INFO, $"删除旧日志文件: {file}");
|
||
}
|
||
}
|
||
else if (File.Exists(file)) // 对于log.1, .log.2,检查最后修改时间
|
||
{
|
||
var lastWriteTime = File.GetLastWriteTime(file);
|
||
if (lastWriteTime < cutoffDate)
|
||
{
|
||
File.Delete(file);
|
||
Write(LogLevel.LOG_INFO, $"删除旧日志备份: {file}");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Write(LogLevel.LOG_ERROR, $"删除旧日志文件失败: {file}", ex);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Write(LogLevel.LOG_ERROR, "清除旧日志失败", ex);
|
||
}
|
||
}
|
||
|
||
// 快捷方法
|
||
public void Debug(string message) => Write(LogLevel.LOG_DEBUG, message);
|
||
public void Info(string message) => Write(LogLevel.LOG_INFO, message);
|
||
public void Warning(string message, Exception ex = null) => Write(LogLevel.LOG_WARNING, message, ex);
|
||
public void Error(string message, Exception ex = null) => Write(LogLevel.LOG_ERROR, message, ex);
|
||
public void XXX(string message, Exception ex = null) => Write(LogLevel.LOG_XXX, message, ex);
|
||
public void MESDebug(string message) => Write(LogLevel.LOG_DEBUG, $"[MES] {message}");
|
||
|
||
#region 私有方法
|
||
|
||
// 获取使能
|
||
private bool IsLevelEnabled(LogLevel level)
|
||
{
|
||
switch (level)
|
||
{
|
||
case LogLevel.LOG_DEBUG: return IsDebugEnabled;
|
||
case LogLevel.LOG_INFO: return IsInfoEnabled;
|
||
case LogLevel.LOG_WARNING: return IsWarningEnabled;
|
||
case LogLevel.LOG_ERROR: return IsErrorEnabled;
|
||
case LogLevel.LOG_XXX: return IsXXXEnabled;
|
||
default: return true;
|
||
}
|
||
}
|
||
|
||
// 生成当前日志文件的完整路径
|
||
private string GetCurrentLogFilePath()
|
||
{
|
||
string dateStamp = DateTime.Now.ToString("yyyyMMdd");
|
||
return Path.Combine(logDir, $"{logPrefix}{dateStamp}.log");
|
||
}
|
||
|
||
// 超过大小,触发轮转(当前备份,重新新建)
|
||
private void CheckFileSize(string logFile)
|
||
{
|
||
if (!File.Exists(logFile)) return;
|
||
|
||
FileInfo fileInfo = new FileInfo(logFile);
|
||
if (fileInfo.Length >= maxFileSize)
|
||
{
|
||
RotateLogFiles(logFile);
|
||
}
|
||
}
|
||
// 实现日志轮转
|
||
private void RotateLogFiles(string currentLogFile)
|
||
{
|
||
for (int i = maxBackupFiles - 1; i >= 1; i--)
|
||
{
|
||
string source = $"{currentLogFile}.{i}";
|
||
string dest = $"{currentLogFile}.{i + 1}";
|
||
|
||
if (File.Exists(source))
|
||
{
|
||
File.Move(source, dest);
|
||
}
|
||
}
|
||
|
||
if (File.Exists(currentLogFile))
|
||
{
|
||
File.Move(currentLogFile, $"{currentLogFile}.1");
|
||
}
|
||
}
|
||
|
||
private string GetCurrentTimeStamp()
|
||
{
|
||
return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
|
||
}
|
||
|
||
private string GetLevelString(LogLevel level)
|
||
{
|
||
switch (level)
|
||
{
|
||
case LogLevel.LOG_DEBUG: return "DEBUG";
|
||
case LogLevel.LOG_INFO: return "INFO";
|
||
case LogLevel.LOG_WARNING: return "WARNING";
|
||
case LogLevel.LOG_ERROR: return "ERROR";
|
||
case LogLevel.LOG_XXX: return "XXX";
|
||
default: return "UNKNOWN";
|
||
}
|
||
}
|
||
|
||
// 主日志系统失败(如磁盘满、权限问题)
|
||
private void HandleLogFailure(Exception ex)
|
||
{
|
||
try
|
||
{
|
||
string fallbackPath = Path.Combine(
|
||
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
|
||
"LL_log_fallback.txt");
|
||
|
||
File.AppendAllText(fallbackPath,
|
||
$"{GetCurrentTimeStamp()} [LOG_ERROR] Failed to write log: {ex.Message}" +
|
||
Environment.NewLine);
|
||
}
|
||
catch { /* 最终保底措施 */ }
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
}
|
||
|
||
// 使用方法 : mxlLog.Instance.Error($"异常 ,行号{ex.StackTrace} ", ex);
|
||
// mxlLog.Instance.IsErrorEnabled = true; 使能错误log
|
||
// mxlLog.Instance.ClearOldLogs(5); 清理日志,仅保留近5天 |