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"); } }