From 32a173465977c4cbd63f4eb48d674fa5ca124b6c Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Tue, 23 Dec 2025 13:08:52 +0800 Subject: [PATCH] =?UTF-8?q?feat(logging):=20=E6=B7=BB=E5=8A=A0=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E7=B3=BB=E7=BB=9F=E5=B9=B6=E9=9B=86=E6=88=90=E5=88=B0?= =?UTF-8?q?=E6=A1=86=E6=9E=B6=E6=A0=B8=E5=BF=83=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现了完整的日志系统,包括ILog接口和ConsoleLogger实现 - 添加了LogConfig配置类和LoggerFactory工厂类 - 在架构、系统、事件、IOC容器等核心组件中集成了日志记录功能 - 添加了NullLogger和CompositeLogger支持 - 创建了详细的日志使用示例和文档 - 实现了日志级别的分类配置和彩色输出功能 --- GFramework.Core/architecture/Architecture.cs | 91 ++++++- GFramework.Core/events/TypeEventSystem.cs | 27 +- GFramework.Core/ioc/IocContainer.cs | 55 +++- GFramework.Core/logging/ConsoleLogger.cs | 128 +++++++++ GFramework.Core/logging/ILog.cs | 45 +++- GFramework.Core/logging/Log.cs | 81 +++++- GFramework.Core/logging/LogConfig.cs | 106 ++++++++ GFramework.Core/logging/LoggerFactory.cs | 131 +++++++++ GFramework.Core/logging/LoggingExample.cs | 263 +++++++++++++++++++ GFramework.Core/logging/NullLogger.cs | 9 +- GFramework.Core/logging/README.md | 207 +++++++++++++++ GFramework.Core/system/AbstractSystem.cs | 11 + GFramework.Godot/logging/GodotLogger.cs | 30 +++ 13 files changed, 1159 insertions(+), 25 deletions(-) create mode 100644 GFramework.Core/logging/ConsoleLogger.cs create mode 100644 GFramework.Core/logging/LogConfig.cs create mode 100644 GFramework.Core/logging/LoggerFactory.cs create mode 100644 GFramework.Core/logging/LoggingExample.cs create mode 100644 GFramework.Core/logging/README.md diff --git a/GFramework.Core/architecture/Architecture.cs b/GFramework.Core/architecture/Architecture.cs index 4eb7e1a..895e875 100644 --- a/GFramework.Core/architecture/Architecture.cs +++ b/GFramework.Core/architecture/Architecture.cs @@ -1,6 +1,7 @@ using GFramework.Core.command; using GFramework.Core.events; using GFramework.Core.ioc; +using GFramework.Core.logging; using GFramework.Core.model; using GFramework.Core.query; using GFramework.Core.system; @@ -93,28 +94,49 @@ public abstract class Architecture : IArchitecture private static readonly Lazy MArchitectureLazy = new(() => { var arch = new T(); + var logger = Log.CreateLogger("Architecture"); + // == Architecture Init == arch.EnterPhase(ArchitecturePhase.Created); + logger.Info($"Architecture {typeof(T).Name} created"); + arch.EnterPhase(ArchitecturePhase.BeforeInit); + logger.Info("Starting architecture initialization"); + // 调用用户实现的初始化 arch.Init(); arch.EnterPhase(ArchitecturePhase.AfterInit); + logger.Info("Architecture initialization completed"); // == Model Init == arch.EnterPhase(ArchitecturePhase.BeforeModelInit); + logger.Info($"Initializing {arch._mModels.Count} models"); + // 初始化所有已注册但尚未初始化的模型 - foreach (var model in arch._mModels) model.Init(); + foreach (var model in arch._mModels) + { + logger.Debug($"Initializing model: {model.GetType().Name}"); + model.Init(); + } arch._mModels.Clear(); arch.EnterPhase(ArchitecturePhase.AfterModelInit); + logger.Info("All models initialized"); // == System Init == arch.EnterPhase(ArchitecturePhase.BeforeSystemInit); + logger.Info($"Initializing {arch._mSystems.Count} systems"); + // 初始化所有已注册但尚未初始化的系统 - foreach (var system in arch._mSystems) system.Init(); + foreach (var system in arch._mSystems) + { + logger.Debug($"Initializing system: {system.GetType().Name}"); + system.Init(); + } arch._mSystems.Clear(); arch.EnterPhase(ArchitecturePhase.AfterSystemInit); + logger.Info("All systems initialized"); // == Finalize == @@ -124,6 +146,7 @@ public abstract class Architecture : IArchitecture arch.EnterPhase(ArchitecturePhase.Ready); // 发送架构生命周期就绪事件 arch.SendEvent(new ArchitectureEvents.ArchitectureLifecycleReadyEvent()); + logger.Info($"Architecture {typeof(T).Name} is ready - all components initialized"); return arch; }, LazyThreadSafetyMode.ExecutionAndPublication); @@ -138,21 +161,32 @@ public abstract class Architecture : IArchitecture /// 当阶段转换不被允许时抛出异常 private void EnterPhase(ArchitecturePhase next) { + var logger = Log.CreateLogger("Architecture"); + if (Options.StrictPhaseValidation && (!ArchitectureConstants.PhaseTransitions.TryGetValue(CurrentPhase, out var allowed) || !allowed.Contains(next))) { // 验证阶段转换是否合法 - throw new InvalidOperationException( - $"Invalid phase transition: {CurrentPhase} -> {next}"); + var errorMsg = $"Invalid phase transition: {CurrentPhase} -> {next}"; + logger.Fatal(errorMsg); + throw new InvalidOperationException(errorMsg); } + var previousPhase = CurrentPhase; CurrentPhase = next; + + if (previousPhase != next) + { + logger.Info($"Architecture phase changed: {previousPhase} -> {next}"); + } + NotifyPhase(next); // 通知所有架构阶段感知对象阶段变更 foreach (var obj in _mContainer.GetAll()) { + logger.Debug($"Notifying phase-aware object {obj.GetType().Name} of phase change to {next}"); obj.OnArchitecturePhase(next); } } @@ -194,23 +228,34 @@ public abstract class Architecture : IArchitecture /// public virtual void Destroy() { + var logger = Log.CreateLogger("Architecture"); + // 检查当前阶段,如果已经处于销毁或已销毁状态则直接返回 if (CurrentPhase >= ArchitecturePhase.Destroying) + { + logger.Warn("Architecture destroy called but already in destroying/destroyed state"); return; + } // 进入销毁阶段并发送销毁开始事件 + logger.Info("Starting architecture destruction"); EnterPhase(ArchitecturePhase.Destroying); SendEvent(new ArchitectureEvents.ArchitectureDestroyingEvent()); // 销毁所有系统组件并清空系统列表 + logger.Info($"Destroying {_allSystems.Count} systems"); foreach (var system in _allSystems) + { + logger.Debug($"Destroying system: {system.GetType().Name}"); system.Destroy(); + } _allSystems.Clear(); // 进入已销毁阶段并发送销毁完成事件 EnterPhase(ArchitecturePhase.Destroyed); SendEvent(new ArchitectureEvents.ArchitectureDestroyedEvent()); + logger.Info("Architecture destruction completed"); } #endregion @@ -223,9 +268,12 @@ public abstract class Architecture : IArchitecture /// 要安装的模块 public void InstallModule(IArchitectureModule module) { + var logger = Log.CreateLogger("Architecture"); + logger.Debug($"Installing module: {module.GetType().Name}"); RegisterLifecycleHook(module); _mContainer.RegisterPlurality(module); module.Install(this); + logger.Info($"Module installed: {module.GetType().Name}"); } #endregion @@ -240,16 +288,28 @@ public abstract class Architecture : IArchitecture /// 要注册的系统实例 public void RegisterSystem(TSystem system) where TSystem : ISystem { + var logger = Log.CreateLogger("Architecture"); + if (CurrentPhase >= ArchitecturePhase.Ready && !Options.AllowLateRegistration) - throw new InvalidOperationException( - "Cannot register system after Architecture is Ready"); + { + var errorMsg = "Cannot register system after Architecture is Ready"; + logger.Error(errorMsg); + throw new InvalidOperationException(errorMsg); + } + + logger.Debug($"Registering system: {typeof(TSystem).Name}"); system.SetArchitecture(this); _mContainer.RegisterPlurality(system); _allSystems.Add(system); if (!_mInited) _mSystems.Add(system); else + { + logger.Debug($"Immediately initializing system: {typeof(TSystem).Name}"); system.Init(); + } + + logger.Info($"System registered: {typeof(TSystem).Name}"); } /// @@ -260,16 +320,28 @@ public abstract class Architecture : IArchitecture /// 要注册的模型实例 public void RegisterModel(TModel model) where TModel : IModel { + var logger = Log.CreateLogger("Architecture"); + if (CurrentPhase >= ArchitecturePhase.Ready && !Options.AllowLateRegistration) - throw new InvalidOperationException( - "Cannot register system after Architecture is Ready"); + { + var errorMsg = "Cannot register model after Architecture is Ready"; + logger.Error(errorMsg); + throw new InvalidOperationException(errorMsg); + } + + logger.Debug($"Registering model: {typeof(TModel).Name}"); model.SetArchitecture(this); _mContainer.RegisterPlurality(model); if (!_mInited) _mModels.Add(model); else + { + logger.Debug($"Immediately initializing model: {typeof(TModel).Name}"); model.Init(); + } + + logger.Info($"Model registered: {typeof(TModel).Name}"); } /// @@ -280,7 +352,10 @@ public abstract class Architecture : IArchitecture /// 要注册的工具实例 public void RegisterUtility(TUtility utility) where TUtility : IUtility { + var logger = Log.CreateLogger("Architecture"); + logger.Debug($"Registering utility: {typeof(TUtility).Name}"); _mContainer.RegisterPlurality(utility); + logger.Info($"Utility registered: {typeof(TUtility).Name}"); } #endregion diff --git a/GFramework.Core/events/TypeEventSystem.cs b/GFramework.Core/events/TypeEventSystem.cs index 6ffbb0f..3a89275 100644 --- a/GFramework.Core/events/TypeEventSystem.cs +++ b/GFramework.Core/events/TypeEventSystem.cs @@ -1,4 +1,6 @@ -namespace GFramework.Core.events; +using GFramework.Core.logging; + +namespace GFramework.Core.events; /// /// TypeEventSystem @@ -10,22 +12,43 @@ public class TypeEventSystem public void Send() where T : new() { + var logger = Log.CreateLogger("Event"); + var eventType = typeof(T); + + logger.Debug($"Sending event: {eventType.Name}"); _mEvents.GetEvent>()?.Trigger(new T()); + logger.Info($"Event sent: {eventType.Name}"); } public void Send(T e) { + var logger = Log.CreateLogger("Event"); + var eventType = typeof(T); + + logger.Debug($"Sending event: {eventType.Name}"); _mEvents.GetEvent>()?.Trigger(e); + logger.Info($"Event sent: {eventType.Name}"); } public IUnRegister Register(Action onEvent) { - return _mEvents.GetOrAddEvent>().Register(onEvent); + var logger = Log.CreateLogger("Event"); + var eventType = typeof(T); + + logger.Debug($"Registering event handler for: {eventType.Name}"); + var result = _mEvents.GetOrAddEvent>().Register(onEvent); + logger.Info($"Event handler registered for: {eventType.Name}"); + return result; } public void UnRegister(Action onEvent) { + var logger = Log.CreateLogger("Event"); + var eventType = typeof(T); + + logger.Debug($"Unregistering event handler for: {eventType.Name}"); var e = _mEvents.GetEvent>(); e?.UnRegister(onEvent); + logger.Info($"Event handler unregistered for: {eventType.Name}"); } } \ No newline at end of file diff --git a/GFramework.Core/ioc/IocContainer.cs b/GFramework.Core/ioc/IocContainer.cs index 8926d96..34bec27 100644 --- a/GFramework.Core/ioc/IocContainer.cs +++ b/GFramework.Core/ioc/IocContainer.cs @@ -1,3 +1,4 @@ +using GFramework.Core.logging; using GFramework.Core.system; namespace GFramework.Core.ioc; @@ -57,20 +58,27 @@ public class IocContainer public void RegisterSingleton(T instance) { var type = typeof(T); + var logger = Log.CreateLogger("IOC"); _lock.EnterWriteLock(); try { if (_frozen) - throw new InvalidOperationException("IocContainer is frozen"); + { + var errorMsg = "IocContainer is frozen"; + logger.Error(errorMsg); + throw new InvalidOperationException(errorMsg); + } if (_typeIndex.TryGetValue(type, out var set) && set.Count > 0) { - throw new InvalidOperationException( - $"Singleton already registered for type: {type.Name}"); + var errorMsg = $"Singleton already registered for type: {type.Name}"; + logger.Error(errorMsg); + throw new InvalidOperationException(errorMsg); } RegisterInternal(type, instance!); + logger.Debug($"Singleton registered: {type.Name}"); } finally { @@ -87,6 +95,8 @@ public class IocContainer public void RegisterPlurality(T instance) { var concreteType = instance!.GetType(); + var logger = Log.CreateLogger("IOC"); + // 获取实例类型直接实现的所有接口,并筛选出可以赋值给T类型的接口 var interfaces = concreteType.GetInterfaces() .Where(typeof(T).IsAssignableFrom); @@ -96,11 +106,13 @@ public class IocContainer { // 注册具体类型 RegisterInternal(concreteType, instance); + logger.Debug($"Registered concrete type: {concreteType.Name}"); // 注册所有匹配的接口类型 foreach (var itf in interfaces) { RegisterInternal(itf, instance); + logger.Debug($"Registered interface: {itf.Name} for {concreteType.Name}"); } } finally @@ -117,7 +129,11 @@ public class IocContainer private void RegisterInternal(Type type, object instance) { if (_frozen) - throw new InvalidOperationException("IocContainer is frozen"); + { + var errorMsg = "IocContainer is frozen"; + Log.CreateLogger("IOC").Error(errorMsg); + throw new InvalidOperationException(errorMsg); + } _objects.Add(instance); @@ -193,14 +209,19 @@ public class IocContainer /// 找到的第一个实例;如果未找到则返回 null public T? Get() where T : class { + var logger = Log.CreateLogger("IOC"); + _lock.EnterReadLock(); try { if (_typeIndex.TryGetValue(typeof(T), out var set) && set.Count > 0) { - return set.First() as T; + var result = set.First() as T; + logger.Debug($"Retrieved instance: {typeof(T).Name}"); + return result; } + logger.Debug($"No instance found for type: {typeof(T).Name}"); return null; } finally @@ -218,14 +239,29 @@ public class IocContainer /// 当没有注册实例或注册了多个实例时抛出 public T GetRequired() where T : class { + var logger = Log.CreateLogger("IOC"); var list = GetAll(); // 根据实例数量进行判断和处理 return list.Count switch { - 0 => throw new InvalidOperationException($"No instance registered for {typeof(T).Name}"), - > 1 => throw new InvalidOperationException($"Multiple instances registered for {typeof(T).Name}"), - _ => list[0] + 0 => + { + var errorMsg = $"No instance registered for {typeof(T).Name}"; + logger.Error(errorMsg); + throw new InvalidOperationException(errorMsg); + }, + > 1 => + { + var errorMsg = $"Multiple instances registered for {typeof(T).Name}"; + logger.Error(errorMsg); + throw new InvalidOperationException(errorMsg); + }, + _ => + { + logger.Debug($"Retrieved required instance: {typeof(T).Name}"); + return list[0]; + } }; } @@ -327,11 +363,14 @@ public class IocContainer /// public void Freeze() { + var logger = Log.CreateLogger("IOC"); + // 获取写锁以确保线程安全的状态修改 _lock.EnterWriteLock(); try { _frozen = true; + logger.Info("IOC Container frozen - no further registrations allowed"); } finally { diff --git a/GFramework.Core/logging/ConsoleLogger.cs b/GFramework.Core/logging/ConsoleLogger.cs new file mode 100644 index 0000000..1b8f5e9 --- /dev/null +++ b/GFramework.Core/logging/ConsoleLogger.cs @@ -0,0 +1,128 @@ +using System; +using System.IO; + +namespace GFramework.Core.logging; + +/// +/// 控制台日志记录器实现,支持日志级别控制和格式化输出 +/// +public sealed class ConsoleLogger : ILog +{ + private readonly LogLevel _minLevel; + private readonly string? _category; + private readonly TextWriter? _writer; + private readonly bool _useColors; + + /// + /// 构造函数 + /// + /// 日志类别 + /// 最小日志级别(默认Info) + /// 输出流(默认控制台) + /// 是否使用颜色输出(默认true) + public ConsoleLogger( + string? category = null, + LogLevel minLevel = LogLevel.Info, + TextWriter? writer = null, + bool useColors = true) + { + _category = category; + _minLevel = minLevel; + _writer = writer ?? Console.Out; + _useColors = useColors && _writer == Console.Out; + } + + /// + /// 记录日志消息 + /// + public void Log(LogLevel level, string message, Exception? exception = null, object? context = null) + { + if (!IsEnabled(level)) + return; + + var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"); + var levelStr = level.ToString().ToUpper().PadRight(7); + var categoryStr = _category != null ? $"[{_category}] " : ""; + var contextStr = context != null ? $" | Context: {FormatContext(context)}" : ""; + var exceptionStr = exception != null ? $"\nException: {exception}" : ""; + + var logMessage = $"[{timestamp}] {levelStr} {categoryStr}{message}{contextStr}{exceptionStr}"; + + if (_useColors) + { + WriteColored(level, logMessage); + } + else + { + _writer.WriteLine(logMessage); + } + } + + /// + /// 检查指定日志级别是否启用 + /// + public bool IsEnabled(LogLevel level) => level >= _minLevel; + + /// + /// 获取当前最小日志级别 + /// + public LogLevel MinLevel => _minLevel; + + private void WriteColored(LogLevel level, string message) + { + var originalColor = Console.ForegroundColor; + try + { + Console.ForegroundColor = GetColor(level); + _writer.WriteLine(message); + } + finally + { + Console.ForegroundColor = originalColor; + } + } + + private ConsoleColor GetColor(LogLevel level) + { + return level switch + { + LogLevel.Trace => ConsoleColor.Gray, + LogLevel.Debug => ConsoleColor.Cyan, + LogLevel.Info => ConsoleColor.White, + LogLevel.Warning => ConsoleColor.Yellow, + LogLevel.Error => ConsoleColor.Red, + LogLevel.Fatal => ConsoleColor.Magenta, + _ => ConsoleColor.White + }; + } + + private string FormatContext(object context) + { + return context switch + { + string str => str, + int num => num.ToString(), + null => "null", + _ => $"{context.GetType().Name}: {context}" + }; + } + + // 快捷方法实现 + public void Info(string msg, object? ctx = null) + => Log(LogLevel.Info, msg, null, ctx); + + public void Error(string msg, Exception? ex = null, object? ctx = null) + => Log(LogLevel.Error, msg, ex, ctx); + + public void Debug(string msg, object? ctx = null) + => Log(LogLevel.Debug, msg, null, ctx); + + public void Trace(string msg, object? ctx = null) + => Log(LogLevel.Trace, msg, null, ctx); + + public void Warn(string msg, object? ctx = null) + => Log(LogLevel.Warning, msg, null, ctx); + + public void Fatal(string msg, object? ctx = null) + => Log(LogLevel.Fatal, msg, null, ctx); +} \ No newline at end of file diff --git a/GFramework.Core/logging/ILog.cs b/GFramework.Core/logging/ILog.cs index 0afc49d..aa37db3 100644 --- a/GFramework.Core/logging/ILog.cs +++ b/GFramework.Core/logging/ILog.cs @@ -20,9 +20,52 @@ public interface ILog ); /// - /// 检查指定日志级别是否已启用 + /// 检查指定日志级别是否启用 /// /// 要检查的日志级别 /// 如果指定级别已启用则返回true,否则返回false bool IsEnabled(LogLevel level); + + /// + /// 记录信息级别日志 + /// + /// 日志消息 + /// 日志上下文信息(可选) + void Info(string msg, object? ctx = null); + + /// + /// 记录错误级别日志 + /// + /// 日志消息 + /// 相关异常对象(可选) + /// 日志上下文信息(可选) + void Error(string msg, Exception? ex = null, object? ctx = null); + + /// + /// 记录调试级别日志 + /// + /// 日志消息 + /// 日志上下文信息(可选) + void Debug(string msg, object? ctx = null); + + /// + /// 记录跟踪级别日志 + /// + /// 日志消息 + /// 日志上下文信息(可选) + void Trace(string msg, object? ctx = null); + + /// + /// 记录警告级别日志 + /// + /// 日志消息 + /// 日志上下文信息(可选) + void Warn(string msg, object? ctx = null); + + /// + /// 记录致命错误级别日志 + /// + /// 日志消息 + /// 日志上下文信息(可选) + void Fatal(string msg, object? ctx = null); } diff --git a/GFramework.Core/logging/Log.cs b/GFramework.Core/logging/Log.cs index 0488bab..48b93fe 100644 --- a/GFramework.Core/logging/Log.cs +++ b/GFramework.Core/logging/Log.cs @@ -5,19 +5,90 @@ /// public static class Log { + private static ILoggerFactory? _factory; + private static LogConfig? _config; + /// - /// 获取或设置当前的日志记录器实例 - /// 默认使用 NullLogger,不输出任何日志 + /// 获取当前的日志记录器实例 /// - public static ILog Instance { get; private set; } = new NullLogger(); + public static ILog Instance { get; private set; } = new ConsoleLogger(null, LogLevel.Info); + + /// + /// 获取当前的日志配置 + /// + public static LogConfig Config => _config ??= new LogConfig(); /// /// 设置日志记录器实例 /// - /// 要设置的日志记录器,如果为 null 则使用 NullLogger + /// 要设置的日志记录器,如果为 null 则使用默认的ConsoleLogger public static void SetLogger(ILog? logger) { - Instance = logger ?? new NullLogger(); + Instance = logger ?? new ConsoleLogger(null, LogLevel.Info); + } + + /// + /// 使用日志工厂创建日志记录器 + /// + /// 日志工厂实例 + public static void SetLoggerFactory(ILoggerFactory factory) + { + _factory = factory ?? throw new ArgumentNullException(nameof(factory)); + Instance = _factory.CreateGlobalLogger(); + } + + /// + /// 使用日志配置初始化日志系统 + /// + /// 日志配置 + public static void Initialize(LogConfig config) + { + _config = config ?? throw new ArgumentNullException(nameof(config)); + _factory = new LoggerFactory(config); + Instance = _factory.CreateGlobalLogger(); + } + + /// + /// 快速配置日志系统 + /// + /// 最小日志级别(默认为Info) + /// 是否启用控制台输出(默认为true) + /// 是否使用彩色输出(默认为true) + /// 是否启用文件输出(默认为false) + /// 日志文件路径(可选) + public static void Configure( + LogLevel minLevel = LogLevel.Info, + bool enableConsole = true, + bool useColors = true, + bool enableFile = false, + string? logFilePath = null) + { + var config = new LogConfig + { + DefaultMinLevel = minLevel, + EnableConsole = enableConsole, + UseColors = useColors, + EnableFile = enableFile, + LogFilePath = logFilePath + }; + + Initialize(config); + } + + /// + /// 创建指定类别的日志记录器 + /// + /// 日志类别 + /// 日志记录器实例 + public static ILog CreateLogger(string category) + { + if (_factory != null) + { + return _factory.Create(category); + } + + // 如果没有设置工厂,使用默认配置 + return new ConsoleLogger(category, Config.GetCategoryLevel(category)); } /// diff --git a/GFramework.Core/logging/LogConfig.cs b/GFramework.Core/logging/LogConfig.cs new file mode 100644 index 0000000..a96c70e --- /dev/null +++ b/GFramework.Core/logging/LogConfig.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace GFramework.Core.logging; + +/// +/// 日志配置类,用于配置日志记录器的行为 +/// +public sealed class LogConfig +{ + /// + /// 获取或设置默认的最小日志级别(默认为Info) + /// + public LogLevel DefaultMinLevel { get; set; } = LogLevel.Info; + + /// + /// 获取或设置是否启用控制台输出(默认为true) + /// + public bool EnableConsole { get; set; } = true; + + /// + /// 获取或设置是否使用彩色输出(默认为true) + /// + public bool UseColors { get; set; } = true; + + /// + /// 获取或设置是否启用文件输出(默认为false) + /// + public bool EnableFile { get; set; } = false; + + /// + /// 获取或设置日志文件路径(当EnableFile为true时使用) + /// + public string? LogFilePath { get; set; } + + /// + /// 取特定类别的日志级别配置 + /// + private readonly Dictionary _categoryLevels = new(); + + /// + /// 设置特定类别的日志级别 + /// + /// 日志类别 + /// 日志级别 + public void SetCategoryLevel(string category, LogLevel level) + { + _categoryLevels[category] = level; + } + + /// + /// 获取类别的日志级别,如果没有特定配置则返回默认级别 + /// + /// 日志类别 + /// 日志级别 + public LogLevel GetCategoryLevel(string? category) + { + if (category != null && _categoryLevels.TryGetValue(category, out var level)) + { + return level; + } + + return DefaultMinLevel; + } + + /// + /// 创建控制台日志记录器 + /// + /// 日志类别 + /// 日志记录器实例 + public ILog CreateConsoleLogger(string? category = null) + { + var level = GetCategoryLevel(category); + return new ConsoleLogger(category, level, null, UseColors); + } + + /// + /// 创建文件日志记录器 + /// + /// 日志类别 + /// 日志记录器实例 + public ILog CreateFileLogger(string? category = null) + { + if (!EnableFile || string.IsNullOrEmpty(LogFilePath)) + return new NullLogger(); + + try + { + var level = GetCategoryLevel(category); + var directory = Path.GetDirectoryName(LogFilePath); + if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + var writer = new StreamWriter(LogFilePath, append: true); + return new ConsoleLogger(category, level, writer, useColors: false); + } + catch + { + // 如果创建文件失败,返回空日志记录器 + return new NullLogger(); + } + } +} \ No newline at end of file diff --git a/GFramework.Core/logging/LoggerFactory.cs b/GFramework.Core/logging/LoggerFactory.cs new file mode 100644 index 0000000..6837327 --- /dev/null +++ b/GFramework.Core/logging/LoggerFactory.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; + +namespace GFramework.Core.logging; + +/// +/// 日志工厂实现,使用LogConfig配置创建日志记录器 +/// +public class LoggerFactory : ILoggerFactory +{ + private readonly LogConfig _config; + private readonly Dictionary _loggers = new(); + + /// + /// 构造函数 + /// + /// 日志配置 + public LoggerFactory(LogConfig config) + { + _config = config ?? throw new ArgumentNullException(nameof(config)); + } + + /// + /// 创建指定类别的日志记录器 + /// + /// 日志类别 + /// 日志记录器实例 + public ILog Create(string category) + { + if (_loggers.TryGetValue(category, out var existingLogger)) + { + return existingLogger; + } + + var logger = CreateLogger(category); + _loggers[category] = logger; + return logger; + } + + /// + /// 创建全局日志记录器实例 + /// + /// 全局日志记录器 + public ILog CreateGlobalLogger() + { + return Create("Global"); + } + + private ILog CreateLogger(string category) + { + var level = _config.GetCategoryLevel(category); + var consoleLogger = new ConsoleLogger(category, level, null, _config.UseColors); + + if (_config.EnableFile && !string.IsNullOrEmpty(_config.LogFilePath)) + { + // 创建一个组合日志记录器,同时输出到控制台和文件 + return new CompositeLogger(category, level, consoleLogger, CreateFileLogger(category)); + } + + return consoleLogger; + } + + private ILog CreateFileLogger(string category) + { + try + { + var level = _config.GetCategoryLevel(category); + var directory = System.IO.Path.GetDirectoryName(_config.LogFilePath); + if (!string.IsNullOrEmpty(directory) && !System.IO.Directory.Exists(directory)) + { + System.IO.Directory.CreateDirectory(directory); + } + + var writer = new System.IO.StreamWriter(_config.LogFilePath!, append: true); + return new ConsoleLogger(category, level, writer, useColors: false); + } + catch + { + return new NullLogger(); + } + } + + /// + /// 组合日志记录器,将日志同时输出到多个目标 + /// + private class CompositeLogger : ILog + { + private readonly string _category; + private readonly LogLevel _minLevel; + private readonly ILog _consoleLogger; + private readonly ILog _fileLogger; + + public CompositeLogger(string category, LogLevel minLevel, ILog consoleLogger, ILog fileLogger) + { + _category = category; + _minLevel = minLevel; + _consoleLogger = consoleLogger; + _fileLogger = fileLogger; + } + + public void Log(LogLevel level, string message, Exception? exception = null, object? context = null) + { + if (!IsEnabled(level)) + return; + + _consoleLogger.Log(level, message, exception, context); + _fileLogger.Log(level, message, exception, context); + } + + public bool IsEnabled(LogLevel level) => level >= _minLevel; + + // 快捷方法实现 + public void Info(string msg, object? ctx = null) + => Log(LogLevel.Info, msg, null, ctx); + + public void Error(string msg, Exception? ex = null, object? ctx = null) + => Log(LogLevel.Error, msg, ex, ctx); + + public void Debug(string msg, object? ctx = null) + => Log(LogLevel.Debug, msg, null, ctx); + + public void Trace(string msg, object? ctx = null) + => Log(LogLevel.Trace, msg, null, ctx); + + public void Warn(string msg, object? ctx = null) + => Log(LogLevel.Warning, msg, null, ctx); + + public void Fatal(string msg, object? ctx = null) + => Log(LogLevel.Fatal, msg, null, ctx); + } +} \ No newline at end of file diff --git a/GFramework.Core/logging/LoggingExample.cs b/GFramework.Core/logging/LoggingExample.cs new file mode 100644 index 0000000..8ce9448 --- /dev/null +++ b/GFramework.Core/logging/LoggingExample.cs @@ -0,0 +1,263 @@ +using GFramework.Core.architecture; +using GFramework.Core.logging; +using GFramework.Core.system; +using GFramework.Core.model; +using GFramework.Core.events; + +namespace GFramework.Core.Examples; + +/// +/// 日志系统使用示例 +/// 演示如何在实际项目中使用GFramework.Core的日志功能 +/// +public class LoggingExample +{ + /// + /// 演示基本的日志配置和使用 + /// + public static void DemonstrateBasicUsage() + { + Console.WriteLine("=== 基本日志使用示例 ==="); + + // 1. 快速配置日志系统(默认Info级别) + Log.Configure( + minLevel: LogLevel.Info, + enableConsole: true, + useColors: true + ); + + // 2. 使用全局日志 + Log.Info("应用程序启动"); + Log.Warn("这是一个警告"); + Log.Error("这是一个错误信息"); + + // 3. 创建特定类别的日志记录器 + var userLogger = Log.CreateLogger("UserService"); + userLogger.Info("用户服务初始化完成", new { ServiceVersion = "1.0" }); + + var dbLogger = Log.CreateLogger("Database"); + dbLogger.Debug("连接数据库", new { Server = "localhost", Database = "MyApp" }); + dbLogger.Info("数据库连接成功"); + + Console.WriteLine(); + } + + /// + /// 演示高级日志配置 + /// + public static void DemonstrateAdvancedConfiguration() + { + Console.WriteLine("=== 高级日志配置示例 ==="); + + // 创建详细的日志配置 + var config = new LogConfig + { + DefaultMinLevel = LogLevel.Info, + EnableConsole = true, + UseColors = true, + EnableFile = true, + LogFilePath = "logs/example-{yyyy-MM-dd}.log" + }; + + // 为不同模块设置不同的日志级别 + config.SetCategoryLevel("Architecture", LogLevel.Info); + config.SetCategoryLevel("IOC", LogLevel.Debug); + config.SetCategoryLevel("Event", LogLevel.Info); + config.SetCategoryLevel("UserService", LogLevel.Debug); + config.SetCategoryLevel("Database", LogLevel.Debug); + config.SetCategoryLevel("Network", LogLevel.Trace); // 最详细的网络日志 + + // 初始化日志系统 + Log.Initialize(config); + + // 演示不同级别的日志记录 + var networkLogger = Log.CreateLogger("Network"); + networkLogger.Trace("网络请求开始", new { Url = "https://api.example.com/users", Method = "GET" }); + networkLogger.Debug("添加请求头", new { Headers = new[] { "Authorization: Bearer xxx", "Content-Type: application/json" } }); + networkLogger.Info("网络请求完成", new { StatusCode = 200, ResponseTime = "120ms" }); + + var userServiceLogger = Log.CreateLogger("UserService"); + userServiceLogger.Debug("查询用户信息", new { UserId = 123 }); + userServiceLogger.Info("用户登录成功", new { UserId = 123, UserName = "张三", LoginTime = DateTime.Now }); + + Console.WriteLine(); + } + + /// + /// 演示在框架组件中使用日志 + /// + public static void DemonstrateFrameworkLogging() + { + Console.WriteLine("=== 框架组件日志示例 ==="); + + // 配置日志系统 + Log.Configure( + minLevel: LogLevel.Info, + enableConsole: true, + useColors: true + ); + + // 创建示例架构 + var architecture = ExampleArchitecture.Instance; + + Console.WriteLine($"架构当前阶段: {architecture.CurrentPhase}"); + + // 注册组件(会自动记录日志) + architecture.RegisterSystem(new ExampleSystem()); + architecture.RegisterModel(new ExampleModel()); + architecture.RegisterUtility(new ExampleUtility()); + + // 发送事件(会自动记录日志) + architecture.SendEvent(new ExampleEvent { Message = "框架日志示例事件" }); + + // 注册事件监听器(会自动记录日志) + architecture.RegisterEvent(evt => + { + Log.CreateLogger("EventListener").Info($"收到事件: {evt.Message}"); + }); + + // 再次发送事件 + architecture.SendEvent(new ExampleEvent { Message = "框架日志示例事件2" }); + + Console.WriteLine(); + } + + /// + /// 演示异常日志记录 + /// + public static void DemonstrateExceptionLogging() + { + Console.WriteLine("=== 异常日志记录示例 ==="); + + Log.Configure(LogLevel.Debug, enableConsole: true, useColors: true); + + var serviceLogger = Log.CreateLogger("UserService"); + + try + { + // 模拟业务逻辑中的异常 + throw new InvalidOperationException("用户数据验证失败"); + } + catch (Exception ex) + { + serviceLogger.Error("用户注册失败", ex, new + { + Operation = "UserRegistration", + UserEmail = "user@example.com", + ValidationErrors = new[] { "邮箱格式无效", "密码强度不足" } + }); + } + + // 演示Fatal级别的使用 + try + { + throw new SystemException("数据库连接完全中断"); + } + catch (Exception ex) + { + serviceLogger.Fatal("系统发生致命错误", ex, new + { + ErrorCode = "DB_CONNECTION_FAILED", + RetryAttempts = 3, + LastRetryTime = DateTime.Now.AddMinutes(-5) + }); + } + + Console.WriteLine(); + } + + /// + /// 演示生产环境日志配置 + /// + public static void DemonstrateProductionConfiguration() + { + Console.WriteLine("=== 生产环境日志配置示例 ==="); + + // 生产环境推荐配置 + var productionConfig = new LogConfig + { + DefaultMinLevel = LogLevel.Info, // 只记录Info及以上级别 + EnableConsole = false, // 生产环境通常不输出到控制台 + UseColors = false, + EnableFile = true, // 启用文件日志 + LogFilePath = "logs/production-{yyyy-MM-dd}.log" + }; + + // 为关键模块设置更详细的日志级别 + productionConfig.SetCategoryLevel("Architecture", LogLevel.Info); + productionConfig.SetCategoryLevel("Security", LogLevel.Debug); // 安全相关日志更详细 + productionConfig.SetCategoryLevel("Payment", LogLevel.Debug); // 支付相关日志更详细 + productionConfig.SetCategoryLevel("IOC", LogLevel.Warning); // 减少IOC调试日志 + + Log.Initialize(productionConfig); + + var securityLogger = Log.CreateLogger("Security"); + securityLogger.Info("用户登录尝试", new { UserId = 456, IpAddress = "192.168.1.100" }); + securityLogger.Warn("密码错误", new { UserId = 456, FailedAttempts = 2 }); + securityLogger.Info("登录成功", new { UserId = 456, SessionId = "sess_abc123" }); + + var paymentLogger = Log.CreateLogger("Payment"); + paymentLogger.Debug("开始处理支付", new { OrderId = "ORD_789", Amount = 99.99m, Currency = "CNY" }); + paymentLogger.Info("支付成功", new { OrderId = "ORD_789", TransactionId = "txn_456" }); + + Console.WriteLine("生产环境日志已配置完成,日志文件将保存到 logs/ 目录"); + Console.WriteLine(); + } +} + +/// +/// 示例架构类 +/// +public class ExampleArchitecture : Architecture +{ + protected override void Init() + { + Log.CreateLogger("Architecture").Info("ExampleArchitecture 初始化"); + } +} + +/// +/// 示例系统 +/// +public class ExampleSystem : AbstractSystem +{ + protected override void OnInit() + { + Log.CreateLogger("System").Info("ExampleSystem 初始化完成"); + } + + protected override void OnDestroy() + { + Log.CreateLogger("System").Info("ExampleSystem 销毁"); + } +} + +/// +/// 示例模型 +/// +public class ExampleModel : AbstractModel +{ + protected override void OnInit() + { + Log.CreateLogger("Model").Info("ExampleModel 初始化完成"); + } +} + +/// +/// 示例工具 +/// +public class ExampleUtility : IUtility +{ + public void DoSomething() + { + Log.CreateLogger("Utility").Debug("ExampleUtility 执行操作"); + } +} + +/// +/// 示例事件 +/// +public class ExampleEvent +{ + public string Message { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/GFramework.Core/logging/NullLogger.cs b/GFramework.Core/logging/NullLogger.cs index 08e33d7..d06cb50 100644 --- a/GFramework.Core/logging/NullLogger.cs +++ b/GFramework.Core/logging/NullLogger.cs @@ -20,5 +20,12 @@ internal sealed class NullLogger : ILog /// 要检查的日志级别 /// 始终返回 false,表示所有日志级别都被禁用 public bool IsEnabled(LogLevel level) => false; -} + // 快捷方法实现(空实现) + public void Info(string msg, object? ctx = null) { } + public void Error(string msg, Exception? ex = null, object? ctx = null) { } + public void Debug(string msg, object? ctx = null) { } + public void Trace(string msg, object? ctx = null) { } + public void Warn(string msg, object? ctx = null) { } + public void Fatal(string msg, object? ctx = null) { } +} diff --git a/GFramework.Core/logging/README.md b/GFramework.Core/logging/README.md new file mode 100644 index 0000000..bc0c0cf --- /dev/null +++ b/GFramework.Core/logging/README.md @@ -0,0 +1,207 @@ +# GFramework.Core 日志系统 + +## 概述 + +GFramework.Core 提供了一个灵活、可配置的日志系统,支持多级别、多类别和多种输出方式的日志记录。默认日志级别为 `Info`,确保框架的关键操作都能被记录下来。 + +## 主要特性 + +- **多级别日志支持**: Trace、Debug、Info、Warning、Error、Fatal +- **类别化日志**: 支持按模块分类记录日志 +- **可配置输出**: 支持控制台输出和文件输出 +- **彩色控制台输出**: 提供不同级别的颜色区分 +- **灵活的级别控制**: 可全局设置或按类别设置不同的日志级别 + +## 快速开始 + +### 1. 基本配置 + +```csharp +using GFramework.Core.logging; + +// 快速配置(推荐) +Log.Configure( + minLevel: LogLevel.Info, // 默认级别为Info + enableConsole: true, // 启用控制台输出 + useColors: true, // 使用彩色输出 + enableFile: false, // 不启用文件输出 + logFilePath: null // 无文件输出 +); + +// 或者使用详细的配置 +var config = new LogConfig +{ + DefaultMinLevel = LogLevel.Info, + EnableConsole = true, + UseColors = true, + EnableFile = true, + LogFilePath = "logs/gframework.log" +}; + +// 设置特定模块的日志级别 +config.SetCategoryLevel("Architecture", LogLevel.Debug); +config.SetCategoryLevel("IOC", LogLevel.Debug); +config.SetCategoryLevel("Event", LogLevel.Info); + +Log.Initialize(config); +``` + +### 2. 使用日志记录 + +```csharp +// 使用全局日志实例 +Log.Info("应用程序启动完成"); +Log.Warn("这是一个警告信息"); +Log.Error("这是一个错误信息", exception); + +// 创建特定类别的日志记录器 +var logger = Log.CreateLogger("MyModule"); + +logger.Debug("调试信息:连接数据库"); +logger.Info("用户登录成功", new { UserId = 123, UserName = "张三" }); +logger.Warn("连接超时,正在重试", new { RetryCount = 2 }); +logger.Error("数据库连接失败", ex, new { DatabaseUrl = "localhost:1433" }); +logger.Fatal("系统发生严重错误", new { SystemState = "Critical" }); +``` + +### 3. 高级配置 + +```csharp +// 创建自定义配置的日志记录器 +var config = new LogConfig +{ + DefaultMinLevel = LogLevel.Warning, // 默认只显示Warning及以上 + EnableConsole = true, + UseColors = true, + EnableFile = true, + LogFilePath = "logs/app-{yyyy-MM-dd}.log" +}; + +// 为不同模块设置不同的日志级别 +config.SetCategoryLevel("Architecture", LogLevel.Info); // 架构信息 +config.SetCategoryLevel("IOC", LogLevel.Debug); // IOC详细日志 +config.SetCategoryLevel("Event", LogLevel.Info); // 事件日志 +config.SetCategoryLevel("System", LogLevel.Info); // 系统日志 +config.SetCategoryLevel("Database", LogLevel.Debug); // 数据库日志 +config.SetCategoryLevel("Network", LogLevel.Trace); // 网络日志(最详细) + +Log.Initialize(config); +``` + +## 日志级别说明 + +- **Trace**: 最详细的跟踪信息,用于调试复杂的执行流程 +- **Debug**: 调试信息,用于开发和测试阶段 +- **Info**: 一般信息,记录重要的业务流程和系统状态 +- **Warning**: 警告信息,可能的问题但不中断程序执行 +- **Error**: 错误信息,影响功能但不致命 +- **Fatal**: 致命错误,导致程序无法继续运行 + +## 框架内部日志 + +GFramework.Core 在以下关键位置自动添加了日志记录: + +### 架构模块 (Architecture) +- 架构初始化流程 +- 组件注册和初始化 +- 生命周期阶段变更 +- 模块安装和卸载 + +### IOC容器 (IOC) +- 对象注册和获取 +- 容器冻结操作 +- 重复注册检测 + +### 事件系统 (Event) +- 事件发送和接收 +- 事件处理器注册和注销 + +### 系统模块 (System) +- 系统初始化和销毁 +- 组件生命周期管理 + +## 输出格式 + +### 控制台输出示例 +``` +[2025-12-23 12:34:56.789] INFO Architecture Architecture initialized +[2025-12-23 12:34:56.790] INFO Architecture Initializing 3 systems +[2025-12-23 12:34:56.791] DEBUG Architecture Initializing system: GameSystem +[2025-12-23 12:34:56.792] INFO Architecture System registered: GameSystem +[2025-12-23 12:34:56.793] INFO Architecture Architecture is ready - all components initialized +``` + +### 日志输出级别颜色 +- **Trace**: 灰色 +- **Debug**: 青色 +- **Info**: 白色 +- **Warning**: 黄色 +- **Error**: 红色 +- **Fatal**: 洋红色 + +## 最佳实践 + +1. **使用合适的日志级别**: + - 使用 `Info` 记录重要业务流程 + - 使用 `Debug` 记录调试信息 + - 使用 `Warning` 记录异常情况 + - 使用 `Error` 记录错误但不影响程序运行 + - 使用 `Fatal` 记录严重错误 + +2. **提供上下文信息**: + ```csharp + Log.Info("用户登录成功", new { UserId = userId, UserName = userName }); + ``` + +3. **异常日志记录**: + ```csharp + try + { + // 业务逻辑 + } + catch (Exception ex) + { + Log.Error("数据库操作失败", ex, new { Operation = "InsertUser", UserId = userId }); + } + ``` + +4. **分类使用日志**: + ```csharp + var dbLogger = Log.CreateLogger("Database"); + var netLogger = Log.CreateLogger("Network"); + + dbLogger.Info("查询用户数据"); + netLogger.Debug("发送HTTP请求"); + ``` + +## 配置建议 + +### 开发环境 +```csharp +Log.Configure( + minLevel: LogLevel.Debug, + enableConsole: true, + useColors: true, + enableFile: false +); +``` + +### 生产环境 +```csharp +var config = new LogConfig +{ + DefaultMinLevel = LogLevel.Info, + EnableConsole = false, // 生产环境通常不需要控制台输出 + UseColors = false, + EnableFile = true, + LogFilePath = "logs/production-{yyyy-MM-dd}.log" +}; + +config.SetCategoryLevel("Architecture", LogLevel.Info); +config.SetCategoryLevel("IOC", LogLevel.Warning); // 减少IOC日志 +config.SetCategoryLevel("Event", LogLevel.Info); + +Log.Initialize(config); +``` + +通过这个日志系统,你可以全面追踪GFramework.Core的执行过程,快速定位问题,并监控应用程序的运行状态。 \ No newline at end of file diff --git a/GFramework.Core/system/AbstractSystem.cs b/GFramework.Core/system/AbstractSystem.cs index 1490b64..8afa3ef 100644 --- a/GFramework.Core/system/AbstractSystem.cs +++ b/GFramework.Core/system/AbstractSystem.cs @@ -1,4 +1,5 @@ using GFramework.Core.architecture; +using GFramework.Core.logging; using GFramework.Core.rule; namespace GFramework.Core.system; @@ -34,7 +35,12 @@ public abstract class AbstractSystem : ISystem /// void ISystem.Init() { + var logger = Log.CreateLogger("System"); + logger.Debug($"Initializing system: {GetType().Name}"); + OnInit(); + + logger.Info($"System initialized: {GetType().Name}"); } /// @@ -42,7 +48,12 @@ public abstract class AbstractSystem : ISystem /// void ISystem.Destroy() { + var logger = Log.CreateLogger("System"); + logger.Debug($"Destroying system: {GetType().Name}"); + OnDestroy(); + + logger.Info($"System destroyed: {GetType().Name}"); } /// diff --git a/GFramework.Godot/logging/GodotLogger.cs b/GFramework.Godot/logging/GodotLogger.cs index 6c0503b..138925e 100644 --- a/GFramework.Godot/logging/GodotLogger.cs +++ b/GFramework.Godot/logging/GodotLogger.cs @@ -46,4 +46,34 @@ public sealed class GodotLogger : ILog /// 日志级别 /// 始终返回 true public bool IsEnabled(LogLevel level) => true; + + public void Info(string msg, object? ctx = null) + { + throw new NotImplementedException(); + } + + public void Error(string msg, Exception? ex = null, object? ctx = null) + { + throw new NotImplementedException(); + } + + public void Debug(string msg, object? ctx = null) + { + throw new NotImplementedException(); + } + + public void Trace(string msg, object? ctx = null) + { + throw new NotImplementedException(); + } + + public void Warn(string msg, object? ctx = null) + { + throw new NotImplementedException(); + } + + public void Fatal(string msg, object? ctx = null) + { + throw new NotImplementedException(); + } }