--- title: 日志(Logging) description: 说明 GFramework.Core.Logging 的日志接口、组合方式与常见使用入口。 --- # 日志(Logging) `GFramework.Core.Logging` 是 Core runtime 的默认日志实现。只加载抽象层时,`LoggerFactoryResolver` 会退回 silent provider;加载 `GFramework.Core` 或在 `ArchitectureConfiguration` 里显式提供 provider 后,日志才会 真正输出。 ## 最小用法 ```csharp using GFramework.Core.Abstractions.Logging; var logger = LoggerFactoryResolver.Provider.CreateLogger("Bootstrap"); logger.Info("Application started"); logger.Warn("Config file missing"); ``` 默认 `ArchitectureConfiguration` 会把 provider 配成 `ConsoleLoggerFactoryProvider`,最小级别是 `Info`。如果你 直接走标准 `Architecture` 启动路径,这条配置会自动生效。 ## 在 Architecture 中调整日志级别 ```csharp using GFramework.Core.Architectures; using GFramework.Core.Abstractions.Logging; using GFramework.Core.Abstractions.Properties; using GFramework.Core.Logging; var configuration = new ArchitectureConfiguration { LoggerProperties = new LoggerProperties { LoggerFactoryProvider = new ConsoleLoggerFactoryProvider { MinLevel = LogLevel.Debug } } }; ``` 如果你只是想减少噪音或临时打开 `Debug`,通常只调 `MinLevel` 就够了。 ## 结构化日志与上下文 默认 Core logger 实现支持 `IStructuredLogger` 和 `LogContext`。当你需要把 `requestId`、`sceneName` 之类的 上下文随异步流透传时,优先用上下文属性,而不是把所有信息拼进字符串。 ```csharp using GFramework.Core.Abstractions.Logging; var logger = LoggerFactoryResolver.Provider.CreateLogger("Matchmaking"); using (LogContext.Push("RequestId", requestId)) { if (logger is IStructuredLogger structured) { structured.Log( LogLevel.Info, "Player matched", ("PlayerId", playerId), ("RoomId", roomId)); } } ``` ## 当前仓库内置的常用实现 - `ConsoleLoggerFactoryProvider` - `ConsoleLoggerFactory` - `CompositeLogger` - `LoggingConfigurationLoader` 如果你需要文件输出、rolling file、async appender 或 JSON formatter,可以先用 `LoggingConfigurationLoader` 读取 `LoggingConfiguration`,再把自定义 `ILoggerFactoryProvider` 挂到 `ArchitectureConfiguration.LoggerProperties.LoggerFactoryProvider` 或 `LoggerFactoryResolver.Provider`。 宿主包也可以提供自己的 appender。Godot 项目如果需要把 Core 日志管线输出到 Godot 控制台,可以引用 `GFramework.Godot.Logging.GodotLogAppender`,再用 `CompositeLogger` 或自定义 factory 把它和文件、JSON、异步输出等 Core 组件组合在同一条调用面下。 ## 组合多个输出目标 需要同时写入宿主控制台和文件时,保留业务侧 `ILogger` 调用面不变,替换 provider 即可。下面的 provider 会为每个 logger 创建一个 `CompositeLogger`,并把同步的 Godot 控制台输出和异步文件输出组合在一起: ```csharp using GFramework.Core.Abstractions.Logging; using GFramework.Core.Logging; using GFramework.Core.Logging.Appenders; using GFramework.Core.Logging.Formatters; using GFramework.Godot.Logging; public sealed class GodotCompositeLoggerFactoryProvider : ILoggerFactoryProvider { private readonly string _filePath; public GodotCompositeLoggerFactoryProvider(string filePath) { _filePath = filePath; } public LogLevel MinLevel { get; set; } = LogLevel.Info; public ILogger CreateLogger(string name) { return new CompositeLogger( name, MinLevel, new GodotLogAppender(), new AsyncLogAppender(new FileAppender(_filePath, new DefaultLogFormatter()))); } } ``` 在 Godot 宿主里,文件路径应先解析成普通文件系统路径,再挂到架构配置: ```csharp using GFramework.Core.Abstractions.Logging; using GFramework.Core.Abstractions.Properties; using GFramework.Core.Architectures; using Godot; var logPath = ProjectSettings.GlobalizePath("user://logs/game.log"); var configuration = new ArchitectureConfiguration { LoggerProperties = new LoggerProperties { LoggerFactoryProvider = new GodotCompositeLoggerFactoryProvider(logPath) { MinLevel = LogLevel.Debug } } }; ``` `GodotLogAppender` 只负责 Godot 控制台落点;文件生命周期、异步缓冲、formatter 与过滤规则仍然来自 Core logging 组件。 ## 什么时候该换 provider 下面这些场景通常不该只靠改 `MinLevel`: - 需要文件输出、rolling file 或 async appender - 需要按 namespace / level 做过滤 - 需要 JSON 格式日志 - 需要组合多个 appender - 需要把输出落到 Godot、Unity 或其他宿主控制台 这时更合理的做法是保留 `ILogger` 调用面不变,只替换 provider / factory / formatter / appender 组合。