Files
CheckDevice/Check.Main/Common/ThreadSafeLogger.cs
17860779768 2e46747ba9 视觉修改
2025-08-25 16:33:58 +08:00

113 lines
4.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
namespace Check.Main.Common
{
/// <summary>
/// 一个线程安全的、基于队列的日志记录器。
/// 使用一个独立的后台线程来处理文件写入,避免业务线程阻塞。
/// </summary>
public static class ThreadSafeLogger
{
// 使用线程安全的队列作为日志消息的缓冲区
private static readonly BlockingCollection<string> _logQueue = new BlockingCollection<string>();
// 日志写入线程
private static Thread _logWriterThread;
private static StreamWriter _logFileWriter;
// 事件用于将格式化后的日志消息广播给UI等监听者
public static event Action<string> OnLogMessage;
/// <summary>
/// 初始化日志记录器,启动后台写入线程。
/// </summary>
public static void Initialize()
{
try
{
string logDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
Directory.CreateDirectory(logDirectory);
string logFileName = $"Log_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.txt";
string logFilePath = Path.Combine(logDirectory, logFileName);
_logFileWriter = new StreamWriter(logFilePath, append: true, encoding: Encoding.UTF8) { AutoFlush = true };
// 创建并启动后台线程
_logWriterThread = new Thread(ProcessLogQueue)
{
IsBackground = true, // 设置为后台线程,这样主程序退出时它会自动终止
Name = "LogWriterThread"
};
_logWriterThread.Start();
Log("日志系统已初始化。");
}
catch (Exception ex)
{
// 如果初始化失败尝试通过事件通知UI
OnLogMessage?.Invoke($"[CRITICAL] 日志系统初始化失败: {ex.Message}");
}
}
/// <summary>
/// 将一条日志消息添加到队列中。这个方法是线程安全的,且执行速度非常快。
/// </summary>
/// <param name="message">原始日志消息。</param>
public static void Log(string message)
{
string formattedMessage = $"[{DateTime.Now:HH:mm:ss.fff}] {message}";
_logQueue.Add(formattedMessage);
}
/// <summary>
/// 后台线程的工作方法。它会持续不断地从队列中取出消息并处理。
/// </summary>
private static void ProcessLogQueue()
{
// GetConsumingEnumerable会阻塞等待直到有新的项加入队列或队列被标记为已完成
foreach (string message in _logQueue.GetConsumingEnumerable())
{
try
{
// 1. 写入文件
_logFileWriter?.WriteLine(message);
// 2. 触发事件通知UI
OnLogMessage?.Invoke(message);
}
catch
{
// 忽略在日志线程本身发生的写入错误
}
}
}
/// <summary>
/// 关闭日志记录器,释放资源。
/// </summary>
public static void Shutdown()
{
Log("日志系统正在关闭...");
// 标记队列不再接受新的项目。这会让ProcessLogQueue中的循环在处理完所有剩余项后自然结束。
_logQueue.CompleteAdding();
// 等待日志线程处理完所有剩余的日志最多等待2秒
_logWriterThread?.Join(2000);
// 关闭文件流
_logFileWriter?.Close();
_logFileWriter?.Dispose();
}
}
}