254 lines
8.8 KiB
C#
254 lines
8.8 KiB
C#
|
|
using HslCommunication.LogNet;
|
|||
|
|
using System.Collections.Concurrent;
|
|||
|
|
using System.IO;
|
|||
|
|
using WinFormsApp.Utils;
|
|||
|
|
|
|||
|
|
namespace guoke
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 日志服务
|
|||
|
|
/// </summary>
|
|||
|
|
public class LogService
|
|||
|
|
{
|
|||
|
|
public static LogService Log;
|
|||
|
|
// 按照文件大小切割日志,指定10M,不限制日志文件数量
|
|||
|
|
public ILogNet logNet { get; set; }
|
|||
|
|
public LogService()
|
|||
|
|
{
|
|||
|
|
LogConfigModel logConfig = new LogConfigModel();
|
|||
|
|
//构建配置
|
|||
|
|
logConfig = ConfigReader.GetObject<LogConfigModel>("Logs");
|
|||
|
|
|
|||
|
|
//创建日志实例
|
|||
|
|
logNet = new LogNetFileSize(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, logConfig.LogFilePath), logConfig.MaxFileSize * 1024 * 1024, logConfig.MaxArchiveFiles);
|
|||
|
|
logNet.SetMessageDegree(logConfig.LogLevel);
|
|||
|
|
// 如果所有的日志在记录之前需要在控制台显示出来
|
|||
|
|
logNet.BeforeSaveToFile += (object sender, HslEventArgs e) =>
|
|||
|
|
{
|
|||
|
|
Console.WriteLine(e.HslMessage.ToString());
|
|||
|
|
};
|
|||
|
|
Log = this;
|
|||
|
|
Log.Info("日志", $"等级:{logConfig.LogLevel},大小:{logConfig.MaxFileSize * 1024 * 1024}B,数量:{logConfig.MaxArchiveFiles}");
|
|||
|
|
}
|
|||
|
|
#region 时间
|
|||
|
|
// 使用并发字典替代普通字典+锁,提高并发性能
|
|||
|
|
private static readonly ConcurrentDictionary<string, DateTime> _loggedMessages =
|
|||
|
|
new ConcurrentDictionary<string, DateTime>();
|
|||
|
|
|
|||
|
|
// 过期时间配置为60秒
|
|||
|
|
private static TimeSpan _expirationTime = TimeSpan.FromSeconds(60);
|
|||
|
|
|
|||
|
|
// 上次清理时间
|
|||
|
|
private static DateTime _lastCleanupTime = DateTime.MinValue;
|
|||
|
|
|
|||
|
|
// 清理间隔设置为过期时间的一半,平衡性能和内存使用
|
|||
|
|
private static TimeSpan _cleanupInterval = TimeSpan.FromSeconds(30);
|
|||
|
|
/// <summary>
|
|||
|
|
/// 记录错误日志(带时间控制)
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="message">日志消息</param>
|
|||
|
|
public void ErrorTime(string key, string message, bool log = true) => LogWithTimeControl(key, message, Error, log);
|
|||
|
|
/// <summary>
|
|||
|
|
/// 记录警告日志(带时间控制)
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="message">日志消息</param>
|
|||
|
|
public void WarnTime(string key, string message, bool log = true) => LogWithTimeControl(key, message, Warn, log);
|
|||
|
|
/// <summary>
|
|||
|
|
/// 记录警告日志(带时间控制)
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="message">日志消息</param>
|
|||
|
|
public void InfoTime(string key, string message, bool log = true) => LogWithTimeControl(key, message, Info, log);
|
|||
|
|
// 带时间控制的通用日志记录方法
|
|||
|
|
private void LogWithTimeControl(string key, string message, Action<string, string> logAction, bool log)
|
|||
|
|
{
|
|||
|
|
if (log)
|
|||
|
|
{
|
|||
|
|
TryCleanupExpiredMessages();
|
|||
|
|
|
|||
|
|
// 检查消息是否已记录且未过期
|
|||
|
|
if (_loggedMessages.TryGetValue(message, out var lastLoggedTime) &&
|
|||
|
|
(DateTime.Now - lastLoggedTime) <= _expirationTime)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
// 记录日志并更新记录时间
|
|||
|
|
logAction($"T:{key}", message);
|
|||
|
|
_loggedMessages.AddOrUpdate(message, DateTime.Now, (_, __) => DateTime.Now);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 尝试清理过期消息
|
|||
|
|
private static void TryCleanupExpiredMessages()
|
|||
|
|
{
|
|||
|
|
var now = DateTime.Now;
|
|||
|
|
|
|||
|
|
// 检查是否达到清理间隔
|
|||
|
|
if ((now - _lastCleanupTime) < _cleanupInterval)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取所有过期键
|
|||
|
|
var expiredKeys = _loggedMessages
|
|||
|
|
.Where(kv => (now - kv.Value) > _expirationTime)
|
|||
|
|
.Select(kv => kv.Key)
|
|||
|
|
.ToList();
|
|||
|
|
|
|||
|
|
// 移除过期键
|
|||
|
|
foreach (var key in expiredKeys)
|
|||
|
|
{
|
|||
|
|
_loggedMessages.TryRemove(key, out _);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_lastCleanupTime = now;
|
|||
|
|
}
|
|||
|
|
#endregion
|
|||
|
|
public void String(string message)
|
|||
|
|
{
|
|||
|
|
logNet.WriteAnyString($"mes:{message}");// 写任意的数据,不受格式化影响
|
|||
|
|
}
|
|||
|
|
public void Debug(string message)
|
|||
|
|
{
|
|||
|
|
logNet.WriteDebug(message);
|
|||
|
|
}
|
|||
|
|
public void Info(string message)
|
|||
|
|
{
|
|||
|
|
logNet.WriteInfo(message);
|
|||
|
|
}
|
|||
|
|
public void Warn(string message)
|
|||
|
|
{
|
|||
|
|
logNet.WriteWarn(message);
|
|||
|
|
}
|
|||
|
|
public void Error(string message)
|
|||
|
|
{
|
|||
|
|
logNet.WriteError(message);
|
|||
|
|
}
|
|||
|
|
public void Fatal(string message)
|
|||
|
|
{
|
|||
|
|
logNet.WriteFatal(message);
|
|||
|
|
}
|
|||
|
|
public void Debug(string key, string message)
|
|||
|
|
{
|
|||
|
|
logNet.WriteDebug(key,message);
|
|||
|
|
}
|
|||
|
|
public void Info(string key, string message)
|
|||
|
|
{
|
|||
|
|
logNet.WriteInfo(key, message);
|
|||
|
|
}
|
|||
|
|
public void Warn(string key, string message)
|
|||
|
|
{
|
|||
|
|
logNet.WriteWarn(key, message);
|
|||
|
|
}
|
|||
|
|
public void Error(string key, string message)
|
|||
|
|
{
|
|||
|
|
logNet.WriteError(key, message);
|
|||
|
|
}
|
|||
|
|
public void Warn(string key, string message,bool log=true)
|
|||
|
|
{
|
|||
|
|
if (log)
|
|||
|
|
logNet.WriteWarn(key, message);
|
|||
|
|
}
|
|||
|
|
public void Error(string key, string message, bool log = true)
|
|||
|
|
{
|
|||
|
|
if (log)
|
|||
|
|
logNet.WriteError(key, message);
|
|||
|
|
}
|
|||
|
|
public void Fatal(string key, string message, bool log = true)
|
|||
|
|
{
|
|||
|
|
if (log)
|
|||
|
|
logNet.WriteFatal(key, message);
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 获取日志文件列表
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="folderPath"></param>
|
|||
|
|
/// <param name="fileExtension"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
private List<string> GetFileNamesWithoutExtension(string folderPath, string fileExtension)
|
|||
|
|
{
|
|||
|
|
List<string> fileNames = new List<string>();
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 检查文件夹是否存在
|
|||
|
|
if (Directory.Exists(folderPath))
|
|||
|
|
{
|
|||
|
|
// 获取指定文件夹内指定后缀的所有文件
|
|||
|
|
string[] files = Directory.GetFiles(folderPath, $"*.{fileExtension}");
|
|||
|
|
|
|||
|
|
foreach (string file in files)
|
|||
|
|
{
|
|||
|
|
// 获取不包含后缀的文件名
|
|||
|
|
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file);
|
|||
|
|
fileNames.Add(fileNameWithoutExtension);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Warn("日志","指定的文件夹不存在");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (Exception e)
|
|||
|
|
{
|
|||
|
|
Error("日志",$"发生错误:{e.Message}");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return fileNames;
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 读取日志
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="filePath"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
private static List<string> ParseLogFile(string filePath)
|
|||
|
|
{
|
|||
|
|
List<string> logs = new List<string>();
|
|||
|
|
string currentLog = string.Empty;
|
|||
|
|
|
|||
|
|
foreach (var line in File.ReadLines(filePath))
|
|||
|
|
{
|
|||
|
|
if (line.Contains("[信息]") || line.Contains("[错误]") || line.Contains("[调试]") || line.Contains("[警告]"))
|
|||
|
|
{
|
|||
|
|
if (!string.IsNullOrEmpty(currentLog))
|
|||
|
|
{
|
|||
|
|
logs.Add(currentLog);
|
|||
|
|
}
|
|||
|
|
currentLog = line;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
if (!string.IsNullOrEmpty(currentLog))
|
|||
|
|
{
|
|||
|
|
currentLog += Environment.NewLine + line;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加最后一个日志条目
|
|||
|
|
if (!string.IsNullOrEmpty(currentLog))
|
|||
|
|
{
|
|||
|
|
logs.Add(currentLog);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return logs;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 日志配置模型
|
|||
|
|
/// </summary>
|
|||
|
|
public class LogConfigModel
|
|||
|
|
{
|
|||
|
|
// 日志文件的最大大小(以m为单位)
|
|||
|
|
public int MaxFileSize { get; set; } = 1;
|
|||
|
|
|
|||
|
|
// 允许的最大存档文件数量
|
|||
|
|
public int MaxArchiveFiles { get; set; } = 100;
|
|||
|
|
/// <summary>
|
|||
|
|
/// 日志文件路径
|
|||
|
|
/// </summary>
|
|||
|
|
public string LogFilePath { get; set; } = "logs";
|
|||
|
|
// 日志记录的最低等级
|
|||
|
|
public HslMessageDegree LogLevel { get; set; }= HslMessageDegree.DEBUG;
|
|||
|
|
}
|
|||
|
|
}
|