主要特性
-
线程安全:使用锁和双队列确保多线程安全以及读写分离
-
批量处理:批量处理日志条目,提高性能
-
日志分级:支持不同级别的日志过滤
-
大小限制:可配置日志文件大小,并且可以清除旧日志,防止日志太多占用空间
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using UnityEditor.Search;
using UnityEngine;
public class ThreadSafeLogger : MonoBehaviour
{
private static ThreadSafeLogger _instance;
public static ThreadSafeLogger Instance
{
get
{
if (_instance == null)
{
var go = new GameObject("ThreadSafeLogger");
_instance = go.AddComponent<ThreadSafeLogger>();
DontDestroyOnLoad(go);
}
return _instance;
}
}
// 日志队列
private Queue<LogEntry> _WaitQueue = new Queue<LogEntry>();
private Queue<LogEntry> _logQueue = new Queue<LogEntry>();
private readonly object _queueLock = new object();
// 日志文件路径
private string _logDirectory;
private string _logFileBaseName;
private StreamWriter _logWriter;
private bool _isRunning = true;
private Thread _logThread;
// 配置
public bool EnableConsoleLog = true;
public bool EnableFileLog = true;
public LogLevel MinimumLogLevel = LogLevel.Info;
// 日志轮转配置
public long MaxLogFileSize = 10 * 1024 * 1024; // 10MB
public int MaxLogFiles = 5; // 最多保留5个日志文件
// 日志级别
public enum LogLevel
{
Debug,
Info,
Warning,
Error,
Critical
}
// 日志条目结构
private struct LogEntry
{
public string Message;
public LogLevel Level;
public string StackTrace;
public DateTime Timestamp;
public LogEntry(string message, LogLevel level, string stackTrace = null)
{
Message = message;
Level = level;
StackTrace = stackTrace;
Timestamp = DateTime.Now;
}
}
void Awake()
{
if (_instance == null)
{
_instance = this;
DontDestroyOnLoad(gameObject);
Initialize();
}
else
{
Destroy(gameObject);
}
}
private void Initialize()
{
if (EnableFileLog)
{
// 创建日志目录
_logDirectory = Path.Combine(Application.persistentDataPath, "Logs");
if (!Directory.Exists(_logDirectory))
Directory.CreateDirectory(_logDirectory);
_logFileBaseName = "log";
OpenLogFile();
}
// 启动日志线程
_logThread = new Thread(LogProcessor)
{
Name = "LogProcessorThread",
IsBackground = true
};
_logThread.Start();
Log("Logger initialized successfully", LogLevel.Info);
}
// 打开日志文件
private void OpenLogFile()
{
string logFilePath = Path.Combine(_logDirectory, $"{_logFileBaseName}.txt");
try
{
_logWriter = new StreamWriter(logFilePath, true, Encoding.UTF8);
_logWriter.AutoFlush = false;
}
catch (Exception e)
{
Debug.LogError($"Failed to create log file: {e.Message}");
EnableFileLog = false;
}
}
// 公共日志方法
public static void LogDebug(string message) => Instance.EnqueueLog(new LogEntry(message, LogLevel.Debug));
public static void LogInfo(string message) => Instance.EnqueueLog(new LogEntry(message, LogLevel.Info));
public static void LogWarning(string message) => Instance.EnqueueLog(new LogEntry(message, LogLevel.Warning));
public static void LogError(string message) => Instance.EnqueueLog(new LogEntry(message, LogLevel.Error));
public static void LogCritical(string message) => Instance.EnqueueLog(new LogEntry(message, LogLevel.Critical));
// 线程安全的日志入队
public void Log(string message, LogLevel level)
{
if (level >= MinimumLogLevel)
{
EnqueueLog(new LogEntry(message, level));
}
}
private void EnqueueLog(LogEntry entry)
{ // 控制台输出
if (EnableConsoleLog)
{
OutputToConsole(entry.Message, entry.Level);
}
lock (_queueLock)
{
_WaitQueue.Enqueue(entry);
}
}
// 日志处理线程
private void LogProcessor()
{
while (_isRunning || _WaitQueue.Count > 0)
{
lock (_queueLock)
{
Queue<LogEntry> temp = _logQueue;
_logQueue = _WaitQueue;
_WaitQueue = temp;
}
if(_logQueue.Count > 0)
{
// 批量获取日志条目
ProcessLogEntries(_logQueue);
_logQueue.Clear();
}
}
// 清理资源
Cleanup();
}
// 处理日志条目
private void ProcessLogEntries(Queue<LogEntry> entries)
{
if(_logWriter == null)
{
Debug.LogError("_logWriter Is Null");
return;
}
foreach (var entry in entries)
{
try
{
string formattedLog = FormatLogEntry(entry);
// 文件输出
if (EnableFileLog && _logWriter != null)
{
_logWriter.WriteLine(formattedLog);
// 检查文件大小,如果超过则轮转
if (_logWriter.BaseStream.Length > MaxLogFileSize)
{
_logWriter.Flush();
RotateLogFiles();
OpenLogFile();
}
else if (entry.Level >= LogLevel.Error)
{
_logWriter.Flush(); // 错误日志立即刷新
}
}
}
catch (Exception e)
{
// 日志系统自身的错误输出到Unity控制台
Debug.LogError($"Log system error: {e.Message}");
}
}
// 定期刷新文件流
if (EnableFileLog && _logWriter != null)
{
try
{
_logWriter.Flush();
}
catch (Exception e)
{
Debug.LogError($"Failed to flush log file: {e.Message}");
}
}
}
// 格式化日志条目
private string FormatLogEntry(LogEntry entry)
{
string levelStr = entry.Level.ToString().ToUpper();
string timestamp = entry.Timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff");
string logMessage = $"[{timestamp}] [{levelStr}] {entry.Message}";
if (!string.IsNullOrEmpty(entry.StackTrace) && entry.Level >= LogLevel.Error)
{
logMessage += $"\nStackTrace: {entry.StackTrace}";
}
return logMessage;
}
// 控制台输出
private void OutputToConsole(string message, LogLevel level)
{
switch (level)
{
case LogLevel.Error:
case LogLevel.Critical:
Debug.LogError(message);
break;
case LogLevel.Warning:
Debug.LogWarning(message);
break;
default:
Debug.Log(message);
break;
}
}
// 日志轮转
private void RotateLogFiles()
{
try
{
// 关闭当前日志文件
_logWriter?.Close();
// 轮转现有的日志文件
for (int i = MaxLogFiles - 1; i >= 0; i--)
{
string currentFile = i == 0 ? Path.Combine(_logDirectory, $"{_logFileBaseName}.txt")
: Path.Combine(_logDirectory, $"{_logFileBaseName}{i}.txt");
string nextFile = Path.Combine(_logDirectory, $"{_logFileBaseName}{i + 1}.txt");
if (File.Exists(currentFile))
{
if (i + 1 > MaxLogFiles)
{
// 超过最大文件数,删除最旧的
File.Delete(currentFile);
}
else
{
// 移动文件
if (File.Exists(nextFile))
File.Delete(nextFile);
File.Move(currentFile, nextFile);
}
}
}
}
catch (Exception e)
{
Debug.LogError($"Log rotation failed: {e.Message}");
}
}
// 清理资源
private void Cleanup()
{
try
{
if (_logWriter != null)
{
_logWriter.Flush();
_logWriter.Close();
_logWriter.Dispose();
_logWriter = null;
}
}
catch (Exception e)
{
Debug.LogError($"Failed to cleanup log writer: {e.Message}");
}
}
void OnApplicationQuit()
{
_isRunning = false;
if (_logThread != null && _logThread.IsAlive)
{
_logThread.Join(3000); // 等待3秒线程结束
}
}
void OnDestroy()
{
_isRunning = false;
Cleanup();
}
}
479

被折叠的 条评论
为什么被折叠?



