using Godot;
using System;
using System.Collections.Generic;
using System.Diagnostics;
///
/// 仿照 Python logging 的 日志系统
///
public static class Logging {
// ========== 日志级别 ==========
public enum Level {
NOSET = 0,
DEBUG = 10,
INFO = 20,
WARNING = 30,
ERROR = 40,
CRITICAL = 50
}
///
/// 日志记录器
///
public class Logger(string name) {
public string Name { get; } = name;
public Level LogLevel { get; set; } = Level.INFO;
private readonly List> _handlers = [];
public void AddHandler(Action 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 _loggers = [];
private static readonly Logger _root = GetLogger("root");
///
/// 获取或创建日志记录器
///
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");
}
}