176 lines
5.8 KiB
C#
176 lines
5.8 KiB
C#
using Godot;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Diagnostics;
|
||
|
||
/// <summary>
|
||
/// 仿照 Python logging 的 日志系统
|
||
/// </summary>
|
||
public static class Logging {
|
||
// ========== 日志级别 ==========
|
||
public enum Level {
|
||
NOSET = 0,
|
||
DEBUG = 10,
|
||
INFO = 20,
|
||
WARNING = 30,
|
||
ERROR = 40,
|
||
CRITICAL = 50
|
||
}
|
||
|
||
/// <summary>
|
||
/// 日志记录器
|
||
/// </summary>
|
||
public class Logger(string name) {
|
||
public string Name { get; } = name;
|
||
public Level LogLevel { get; set; } = Level.INFO;
|
||
private readonly List<Action<Level, string, string, string>> _handlers = [];
|
||
|
||
public void AddHandler(Action<Level, string, string, string> handler) =>
|
||
_handlers.Add(handler);
|
||
|
||
// ===== 日志方法 =====
|
||
public void Debug(string fmt, params object[] args) =>
|
||
Log(Level.DEBUG, fmt, false, args);
|
||
|
||
public void Info(string fmt, params object[] args) =>
|
||
Log(Level.INFO, fmt, false, args);
|
||
|
||
public void Warning(string fmt, params object[] args) =>
|
||
Log(Level.WARNING, fmt, false, args);
|
||
|
||
public void Error(string fmt, params object[] args) =>
|
||
Log(Level.ERROR, fmt, false, args);
|
||
|
||
public void Critical(string fmt, params object[] args) =>
|
||
Log(Level.CRITICAL, fmt, true, args);
|
||
|
||
public void Exception(string fmt, Exception e, params object[] args) {
|
||
// TODO for godot not support /r/n yet
|
||
string stackTrace = e.StackTrace?.Replace("\r\n", "\n") ?? "";
|
||
string exceptionMessage = $"{fmt}\n{e.Message}\n{stackTrace}";
|
||
|
||
Log(Level.ERROR, exceptionMessage, false, args);
|
||
}
|
||
|
||
// ===== 核心Log方法 =====
|
||
private void Log(Level level, string fmt, bool isExec = false, params object[] args) {
|
||
if (level < LogLevel) return;
|
||
|
||
// 格式化消息
|
||
string message = args.Length > 0 ?
|
||
string.Format(fmt, args) : fmt;
|
||
|
||
// 添加执行堆栈信息
|
||
string stackInfo = isExec ?
|
||
$"\nExecution Stack:\n{System.Environment.StackTrace}" : "";
|
||
|
||
// 构建前缀
|
||
string prefix = $"[{DateTime.Now:HH:mm:ss}] [{level}] [{Name}]";
|
||
|
||
// 调用所有处理器
|
||
foreach (var handler in _handlers) {
|
||
handler(level, prefix, message, stackInfo);
|
||
}
|
||
}
|
||
|
||
[Conditional("DEBUG")]
|
||
public void Assert(bool condition, string fmt, params object[] args) {
|
||
if (condition) return;
|
||
string message = args.Length > 0 ?
|
||
string.Format(fmt, args) : fmt;
|
||
Error("Assertion Failed: " + message, true);
|
||
|
||
if (Debugger.IsAttached)
|
||
Debugger.Break();
|
||
|
||
System.Diagnostics.Debug.Assert(condition, message);
|
||
}
|
||
}
|
||
|
||
// ========== 全局管理器 ==========
|
||
private static readonly Dictionary<string, Logger> _loggers = [];
|
||
private static readonly Logger _root = GetLogger("root");
|
||
|
||
/// <summary>
|
||
/// 获取或创建日志记录器
|
||
/// </summary>
|
||
public static Logger GetLogger(string name = "root") {
|
||
if (!_loggers.TryGetValue(name, out var logger)) {
|
||
logger = new Logger(name);
|
||
logger.AddHandler(DefaultHandler);
|
||
_loggers[name] = logger;
|
||
}
|
||
return logger;
|
||
}
|
||
|
||
// ========== 优化的默认处理器 ==========
|
||
private static void DefaultHandler(Level level, string prefix, string message, string stackInfo) {
|
||
// 构建完整日志消息
|
||
string fullMessage = $"{prefix} {message}{stackInfo}";
|
||
|
||
switch (level) {
|
||
case Level.DEBUG:
|
||
// DEBUG级别:使用GD.Print + OS条件
|
||
if (OS.IsStdOutVerbose())
|
||
GD.Print(fullMessage);
|
||
break;
|
||
|
||
case Level.INFO:
|
||
// INFO级别:使用GD.Print
|
||
GD.Print(fullMessage);
|
||
break;
|
||
|
||
case Level.WARNING:
|
||
// WARNING级别:使用PrintRich + 黄色
|
||
GD.PrintRich($"[color=yellow]{fullMessage}[/color]");
|
||
break;
|
||
|
||
case Level.ERROR:
|
||
// ERROR级别:使用GD.PrintErr
|
||
GD.PrintRich($"[color=red][b]{fullMessage}[/b][/color]");
|
||
// GD.PrintErr(fullMessage);
|
||
break;
|
||
|
||
case Level.CRITICAL:
|
||
// CRITICAL级别:使用GD.PushError
|
||
GD.PushError(fullMessage);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// ========== 快捷操作方式 ==========
|
||
public static void Debug(string fmt, params object[] args) =>
|
||
_root.Debug(fmt, args);
|
||
|
||
public static void Info(string fmt, params object[] args) =>
|
||
_root.Info(fmt, args);
|
||
|
||
public static void Warning(string fmt, params object[] args) =>
|
||
_root.Warning(fmt, args);
|
||
|
||
public static void Error(string fmt, params object[] args) =>
|
||
_root.Error(fmt, args);
|
||
|
||
public static void Critical(string fmt, params object[] args) =>
|
||
_root.Critical(fmt, args);
|
||
|
||
// ===== C语言风格的宏断言快捷方式 =====
|
||
[Conditional("DEBUG")]
|
||
public static void Assert(bool condition, string fmt, params object[] args) =>
|
||
_root.Assert(condition, fmt, args);
|
||
|
||
[Conditional("DEBUG")]
|
||
public static void TestPrint() {
|
||
GD.Print("GameSystem Singleton Initialized");
|
||
GD.PrintErr("This is a print error");
|
||
GD.PushWarning("This is a warning");
|
||
GD.PushError("This is a error");
|
||
Debug("This is a debug");
|
||
Info("This is a info");
|
||
Warning("This is a warn");
|
||
Error("This is a error");
|
||
Critical("This is a critical");
|
||
Assert(true, "This is a assert");
|
||
}
|
||
}
|