mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-07 00:39:00 +08:00
docs(godot): 收口日志系统文档
- 重写 docs/zh-CN/godot/logging.md,按当前 provider、控制台输出语义与 [Log] 边界整理采用路径 - 更新 documentation-governance-and-refresh 的 tracking 与 trace,推进恢复点到 RP-017 并记录验证结果
This commit is contained in:
parent
3ba1e3f202
commit
76e7f68544
@ -7,7 +7,7 @@
|
||||
|
||||
## 当前恢复点
|
||||
|
||||
- 恢复点编号:`DOCUMENTATION-GOVERNANCE-REFRESH-RP-016`
|
||||
- 恢复点编号:`DOCUMENTATION-GOVERNANCE-REFRESH-RP-017`
|
||||
- 当前阶段:`Phase 3`
|
||||
- 当前焦点:
|
||||
- 已建立统一公开 skill:`.agents/skills/gframework-doc-refresh/`
|
||||
@ -17,14 +17,16 @@
|
||||
- `docs/zh-CN/godot/scene.md` 与 `docs/zh-CN/godot/ui.md` 已按当前 factory / registry / root / source-generator wiring 重写完成
|
||||
- `docs/zh-CN/godot/signal.md` 已按当前 `Signal(...)` / `SignalBuilder` / `[BindNodeSignal]` 分工重写完成
|
||||
- `docs/zh-CN/godot/extensions.md` 已按当前 `GodotPathExtensions`、`NodeExtensions`、`SignalFluentExtensions` 与 `UnRegisterExtension` 重写完成
|
||||
- 下一轮高优先级页面转为 `docs/zh-CN/godot/logging.md`
|
||||
- `docs/zh-CN/godot/logging.md` 已按当前 provider / factory / logger 结构、Godot 控制台输出语义与 CoreGrid 架构接线重写完成
|
||||
- 下一轮高优先级工作转为评估 Godot 栏目当前 active 恢复点是否可以收口并迁入 archive
|
||||
|
||||
## 当前状态摘要
|
||||
|
||||
- 文档治理规则已收口到仓库规范,README、站点入口与采用链路不再依赖旧文档自证
|
||||
- 高优先级模块入口、`core` 关键专题页与 `tutorials/godot-integration.md` 已回到“以源码 / 测试 / README 为准”的状态
|
||||
- `docs/zh-CN/godot/index.md`、`architecture.md`、`scene.md` 与 `ui.md` 已完成当前实现收口
|
||||
- 当前主题仍是 active topic,因为 `docs/zh-CN/godot/logging.md` 及其与运行时扩展页的交叉引用仍需复核,Godot 文档链路尚未完全收口
|
||||
- 当前主题仍是 active topic,因为 Godot 栏目本轮已完成 `logging.md` 收口,但仍需确认是否可以把当前阶段历史迁入
|
||||
`archive/`,并在下一次推送后跟进 PR #268 的 review 线程收敛情况
|
||||
|
||||
## 当前活跃事实
|
||||
|
||||
@ -93,6 +95,10 @@
|
||||
- 本轮已执行 `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/signal.md` 与
|
||||
`bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/extensions.md`,两页聚焦校验通过
|
||||
- 本轮再次执行 `cd docs && bun run build` 通过,当前 Godot signal / extensions 页面改动没有破坏站点构建
|
||||
- `docs/zh-CN/godot/logging.md` 已改成“当前公开入口、最小接入路径、Godot 控制台输出语义、`[Log]` 协作边界、当前限制”的结构,
|
||||
不再把直接改写 `LoggerFactoryResolver.Provider`、`AbstractGodotModule` 或 Godot 专用日志 API 写成默认接入模型
|
||||
- 本轮已执行 `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/logging.md` 与
|
||||
`cd docs && bun run build`,logging 页面聚焦校验与站点构建继续通过
|
||||
- `.agents/skills/gframework-doc-refresh/SKILL.md` 已改成标准 YAML frontmatter skill,并明确支持模块输入、证据顺序、输出优先级与验证步骤
|
||||
- `.agents/skills/gframework-doc-refresh/SKILL.md` 的 `description` 已加引号,修复 `Recommended command:` 中冒号导致的
|
||||
invalid YAML skill 加载警告
|
||||
@ -113,10 +119,9 @@
|
||||
`godot-project-generator.md`、`get-node-generator.md`、`bind-node-signal-generator.md` 与 `auto-register-exported-collections-generator.md`
|
||||
已完成收口;
|
||||
继续按源码、测试、`*.csproj` 与 `ai-libs/` 下已验证参考实现核对剩余 Godot 相关页面,不把旧文档当事实来源
|
||||
- Godot logging 专题页失真风险:`docs/zh-CN/godot/logging.md` 仍可能沿用旧扩展页引用和过时运行时说明,把已经收口的
|
||||
signal / extensions / index 页重新带偏
|
||||
- 缓解措施:`signal.md` 与 `extensions.md` 已完成收口;下一轮优先按当前日志 API、Godot 运行时边界与真实交叉链接复核
|
||||
`logging.md`
|
||||
- Godot 栏目归档过早风险:虽然 `logging.md` 已完成收口,但如果在推送前就把当前阶段过早归档,后续 review 跟进会缺少
|
||||
清晰的 active 恢复入口
|
||||
- 缓解措施:先保留当前 topic 为 active;待确认本轮页面集与 PR #268 的 review 跟进节奏后,再决定是否迁入 `archive/`
|
||||
- 采用路径误导风险:根聚合包与模块边界若再次被写错,会继续误导消费者的包选择
|
||||
- 缓解措施:保持“源码与包关系优先”的证据顺序,改动采用说明时同步核对包依赖与生成器 wiring
|
||||
- 模块映射不全风险:统一 skill 若遗漏模块别名、测试项目或 docs 栏目映射,会让后续扫描阶段直接失焦
|
||||
@ -170,11 +175,12 @@
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/ui.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/signal.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/extensions.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/logging.md`
|
||||
- `rg -n "GodotSceneRouter|GodotUiRouter|CreateSignalBuilder|GetNodeX|InstallGodotModule\(" docs/zh-CN/godot -S`
|
||||
- `cd docs && bun run build`
|
||||
|
||||
## 下一步
|
||||
|
||||
1. 优先复核 `docs/zh-CN/godot/logging.md`,确认它不会把已收口的 signal / extensions / runtime 边界重新写偏
|
||||
2. 视 `logging.md` 复核结果,决定是否可以把 Godot 栏目的 active 恢复点收口并准备归档本阶段历史
|
||||
1. 评估当前 Godot 栏目页面集是否已足够稳定,决定是否把本阶段 active 恢复点收口并迁入 `archive/`
|
||||
2. 如需继续保持 active,优先精简 tracking / trace,只保留归档决策、当前风险与下一次 PR follow-up 入口
|
||||
3. 下一次推送后重新执行 `$gframework-pr-review`,确认 PR #268 的 CodeRabbit / Greptile open thread 是否按预期收敛
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
## 2026-04-22
|
||||
|
||||
### 当前恢复点:RP-016
|
||||
### 当前恢复点:RP-017
|
||||
|
||||
- 本轮从 PR #268 的最新 review 数据恢复,未发现失败检查;CTRF 报告显示 2139 个测试全部通过
|
||||
- 本轮复核确认当前 PR 的 latest-head open thread 同时来自 `coderabbitai[bot]` 与 `greptile-apps[bot]`
|
||||
@ -42,6 +42,11 @@
|
||||
生命周期边界、当前边界”,不再继续宣称存在覆盖所有 Godot 场景的万能扩展层
|
||||
- 本轮复核 `ai-libs/CoreGrid` 的动态绑定用法后,明确把 fluent API 定位为“动态对象 / 动态 signal 的运行时连接”,而把静态控件绑定继续归到
|
||||
`[BindNodeSignal]` 生成器链路
|
||||
- 本轮已重写 `docs/zh-CN/godot/logging.md`,把内容收口为“当前 provider / factory / logger 结构、最小接入路径、
|
||||
Godot 控制台输出语义、`[Log]` 协作边界、当前限制”,不再把直接改全局 provider 或 `AbstractGodotModule` 写成默认采用路径
|
||||
- 本轮额外复核 `GFramework.Godot/Logging/*.cs`、`GFramework.Core.Abstractions/Logging/LoggerFactoryResolver.cs`、
|
||||
`GFramework.Core/Logging/CachedLoggerFactory.cs` 与 `ai-libs/CoreGrid/global/GameEntryPoint.cs`,确认当前推荐接法应以
|
||||
`ArchitectureConfiguration.LoggerProperties.LoggerFactoryProvider` 为主,而不是先写 `LoggerFactoryResolver.Provider = ...`
|
||||
|
||||
### 当前决策
|
||||
|
||||
@ -62,7 +67,7 @@
|
||||
- `signal.md` 已明确为 `Signal(...)` / `SignalBuilder` 的轻量 fluent 包装说明页,不再继续混入生成器职责
|
||||
- `extensions.md` 已明确限制在 `GodotPathExtensions`、`NodeExtensions`、`SignalFluentExtensions` 与 `UnRegisterExtension`
|
||||
这四组当前存在的扩展
|
||||
- Godot 栏目的下一轮优先级转为 `logging.md`;后续如果它仍复用旧扩展页话术,会重新污染已收口的入口页
|
||||
- `logging.md` 已完成收口;下一轮优先级转为评估当前 Godot 栏目恢复点是否可以迁入 `archive/`,并保留 PR review follow-up 入口
|
||||
|
||||
### 验证
|
||||
|
||||
@ -87,11 +92,12 @@
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/ui.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/signal.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/extensions.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/logging.md`
|
||||
- `rg -n "GodotSceneRouter|GodotUiRouter|CreateSignalBuilder|GetNodeX|InstallGodotModule\(" docs/zh-CN/godot -S`
|
||||
- `cd docs && bun run build`
|
||||
|
||||
### 下一步
|
||||
|
||||
1. 优先复核 `docs/zh-CN/godot/logging.md`,确认它的 API 说明与交叉链接不会把 signal / extensions / runtime 边界重新写偏
|
||||
2. 视 `logging.md` 复核结果,决定是否可以把当前 Godot 栏目恢复点收口并迁入 archive
|
||||
1. 评估当前 Godot 栏目页面集是否已足够稳定,决定是否把当前恢复点收口并迁入 `archive/`
|
||||
2. 如暂不归档,先把 active tracking / trace 进一步压缩到归档决策、当前风险与 PR 跟进入口
|
||||
3. 下一次推送后重新执行 `$gframework-pr-review`,确认 PR #268 的 CodeRabbit / Greptile open thread 是否关闭或减少
|
||||
|
||||
@ -1,43 +1,40 @@
|
||||
---
|
||||
title: Godot 日志系统
|
||||
description: Godot 日志系统提供了 GFramework 日志功能与 Godot 引擎控制台的完整集成。
|
||||
description: 以当前 GFramework.Godot.Logging 源码与 CoreGrid 接线为准,说明 Godot 日志 provider、控制台输出语义与接入边界。
|
||||
---
|
||||
|
||||
# Godot 日志系统
|
||||
|
||||
## 概述
|
||||
`GFramework.Godot` 当前的日志能力很收敛:它不是一套独立于 Core 的新日志框架,而是把现有 `ILogger` 调用面接到
|
||||
Godot 控制台。
|
||||
|
||||
Godot 日志系统是 GFramework.Godot 中连接框架日志功能与 Godot 引擎控制台的核心组件。它提供了与 Godot
|
||||
控制台的深度集成,支持彩色输出、多级别日志记录,以及与 GFramework 日志系统的无缝对接。
|
||||
换句话说,Godot 侧真正新增的是 provider / factory / logger 这层输出适配,而不是新的日志 API。业务代码仍然继续使用
|
||||
`LoggerFactoryResolver.Provider.CreateLogger(...)` 或 `[Log]` 生成的 `ILogger` 字段。
|
||||
|
||||
通过 Godot 日志系统,你可以在 Godot 项目中使用统一的日志接口,日志会自动输出到 Godot 编辑器控制台,并根据日志级别使用不同的颜色和输出方式。
|
||||
## 当前公开入口
|
||||
|
||||
**主要特性**:
|
||||
### `GodotLogger`
|
||||
|
||||
- 与 Godot 控制台深度集成
|
||||
- 支持彩色日志输出
|
||||
- 多级别日志记录(Trace、Debug、Info、Warning、Error、Fatal)
|
||||
- 日志缓存机制
|
||||
- 时间戳和格式化支持
|
||||
- 异常信息记录
|
||||
|
||||
## 核心概念
|
||||
|
||||
### GodotLogger
|
||||
|
||||
`GodotLogger` 是 Godot 平台的日志记录器实现,继承自 `AbstractLogger`:
|
||||
`GodotLogger` 继承自 `AbstractLogger`,负责把日志写到 Godot 的输出 API:
|
||||
|
||||
```csharp
|
||||
public sealed class GodotLogger : AbstractLogger
|
||||
{
|
||||
public GodotLogger(string? name = null, LogLevel minLevel = LogLevel.Info);
|
||||
protected override void Write(LogLevel level, string message, Exception? exception);
|
||||
}
|
||||
public sealed class GodotLogger(
|
||||
string? name = null,
|
||||
LogLevel minLevel = LogLevel.Info)
|
||||
: AbstractLogger(name ?? RootLoggerName, minLevel)
|
||||
```
|
||||
|
||||
### GodotLoggerFactory
|
||||
当前实现里的几个关键语义:
|
||||
|
||||
`GodotLoggerFactory` 用于创建 Godot 日志记录器实例:
|
||||
- 时间戳使用 `DateTime.UtcNow`
|
||||
- 输出前缀格式是 `[yyyy-MM-dd HH:mm:ss.fff] LEVEL [LoggerName]`
|
||||
- `exception` 不会被单独结构化处理,而是直接追加到消息后面
|
||||
- `Trace` / `Debug` 走 `GD.PrintRich(...)`
|
||||
- `Info` / `Warning` / `Error` / `Fatal` 分别走 Godot 自身的普通、警告和错误输出通道
|
||||
|
||||
### `GodotLoggerFactory`
|
||||
|
||||
`GodotLoggerFactory` 只负责按名称和最小级别创建 `GodotLogger`:
|
||||
|
||||
```csharp
|
||||
public class GodotLoggerFactory : ILoggerFactory
|
||||
@ -46,9 +43,11 @@ public class GodotLoggerFactory : ILoggerFactory
|
||||
}
|
||||
```
|
||||
|
||||
### GodotLoggerFactoryProvider
|
||||
它本身不做缓存,也不额外增加过滤规则。
|
||||
|
||||
`GodotLoggerFactoryProvider` 提供日志工厂实例,并支持日志缓存:
|
||||
### `GodotLoggerFactoryProvider`
|
||||
|
||||
`GodotLoggerFactoryProvider` 是当前最常用的接入点:
|
||||
|
||||
```csharp
|
||||
public sealed class GodotLoggerFactoryProvider : ILoggerFactoryProvider
|
||||
@ -58,571 +57,144 @@ public sealed class GodotLoggerFactoryProvider : ILoggerFactoryProvider
|
||||
}
|
||||
```
|
||||
|
||||
## 基本用法
|
||||
它内部用 `CachedLoggerFactory` 包装 `GodotLoggerFactory`。缓存 key 由 `name` 和 `MinLevel` 共同组成,所以:
|
||||
|
||||
### 配置 Godot 日志系统
|
||||
- 同名、同 `MinLevel` 的 logger 会复用实例
|
||||
- 调整 `MinLevel` 后,新创建的 logger 会走新的缓存 key
|
||||
- 已经持有的旧 logger 不会被原地改写
|
||||
|
||||
在架构初始化时配置日志提供程序:
|
||||
## 最小接入路径
|
||||
|
||||
### 1. 在 `ArchitectureConfiguration` 中挂上 Godot provider
|
||||
|
||||
当前仓库里更稳的接法,不是到处直接改全局 `LoggerFactoryResolver.Provider`,而是在架构配置里显式提供
|
||||
`LoggerProperties.LoggerFactoryProvider`。`ai-libs/CoreGrid/global/GameEntryPoint.cs` 现在就是这样接的。
|
||||
|
||||
```csharp
|
||||
using GFramework.Godot.Architecture;
|
||||
using GFramework.Godot.Logging;
|
||||
using GFramework.Core.Logging;
|
||||
using GFramework.Core.Abstractions.Environment;
|
||||
using GFramework.Core.Abstractions.Logging;
|
||||
using GFramework.Core.Abstractions.Properties;
|
||||
using GFramework.Core.Architectures;
|
||||
using GFramework.Godot.Logging;
|
||||
|
||||
public class GameArchitecture : AbstractArchitecture
|
||||
{
|
||||
public static GameArchitecture Interface { get; private set; }
|
||||
|
||||
public GameArchitecture()
|
||||
var architecture = new GameArchitecture(
|
||||
new ArchitectureConfiguration
|
||||
{
|
||||
Interface = this;
|
||||
|
||||
// 配置 Godot 日志系统
|
||||
LoggerFactoryResolver.Provider = new GodotLoggerFactoryProvider
|
||||
LoggerProperties = new LoggerProperties
|
||||
{
|
||||
MinLevel = LogLevel.Debug // 设置最小日志级别
|
||||
};
|
||||
}
|
||||
LoggerFactoryProvider = new GodotLoggerFactoryProvider
|
||||
{
|
||||
MinLevel = LogLevel.Debug
|
||||
}
|
||||
}
|
||||
},
|
||||
environment);
|
||||
|
||||
protected override void InstallModules()
|
||||
{
|
||||
var logger = LoggerFactoryResolver.Provider.CreateLogger("GameArchitecture");
|
||||
logger.Info("游戏架构初始化开始");
|
||||
|
||||
RegisterModel(new PlayerModel());
|
||||
RegisterSystem(new GameplaySystem());
|
||||
|
||||
logger.Info("游戏架构初始化完成");
|
||||
}
|
||||
}
|
||||
architecture.Initialize();
|
||||
```
|
||||
|
||||
### 创建和使用日志记录器
|
||||
这样做的好处是:
|
||||
|
||||
- 日志 provider 和架构启动配置放在同一个入口
|
||||
- 不会把“Godot 控制台输出”误写成全局静态默认前提
|
||||
- 和 `ArchitectureConfiguration` 默认使用 `ConsoleLoggerFactoryProvider` 的 Core 接线方式保持一致
|
||||
|
||||
### 2. 业务代码继续使用标准 `ILogger`
|
||||
|
||||
配置好 provider 之后,Godot 节点、System、Model、router、factory 都继续通过统一入口拿 logger:
|
||||
|
||||
```csharp
|
||||
using Godot;
|
||||
using GFramework.Core.Logging;
|
||||
using GFramework.Core.Abstractions.Logging;
|
||||
using GFramework.Core.Logging;
|
||||
using Godot;
|
||||
|
||||
public partial class Player : CharacterBody2D
|
||||
public partial class SettingsPanel : Control
|
||||
{
|
||||
private ILogger _logger;
|
||||
private static readonly ILogger Log =
|
||||
LoggerFactoryResolver.Provider.CreateLogger(nameof(SettingsPanel));
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
// 创建日志记录器
|
||||
_logger = LoggerFactoryResolver.Provider.CreateLogger("Player");
|
||||
|
||||
_logger.Info("玩家初始化");
|
||||
_logger.Debug("玩家位置: {0}", Position);
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (_logger.IsDebugEnabled())
|
||||
{
|
||||
_logger.Debug("玩家速度: {0}", Velocity);
|
||||
}
|
||||
}
|
||||
|
||||
private void TakeDamage(float damage)
|
||||
{
|
||||
_logger.Warn("玩家受到伤害: {0}", damage);
|
||||
}
|
||||
|
||||
private void OnError()
|
||||
{
|
||||
_logger.Error("玩家状态异常");
|
||||
Log.Info("SettingsPanel ready.");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 记录不同级别的日志
|
||||
如果你已经在用 `GFramework.Core.SourceGenerators`,也可以继续让 `[Log]` 生成字段。Godot provider 只改变输出落点,
|
||||
不会改变 `[Log]` 的生成契约。
|
||||
|
||||
### 3. Scene / UI 迁移日志会自动复用同一套 provider
|
||||
|
||||
`GFramework.Game.Scene.Handler.LoggingTransitionHandler` 和
|
||||
`GFramework.Game.UI.Handler.LoggingTransitionHandler` 都是普通 `ILogger` 使用者。只要当前架构挂的是
|
||||
`GodotLoggerFactoryProvider`,这些迁移日志就会直接进 Godot 控制台。
|
||||
|
||||
```csharp
|
||||
var logger = LoggerFactoryResolver.Provider.CreateLogger("GameSystem");
|
||||
using GFramework.Game.Scene.Handler;
|
||||
using GFramework.Game.UI.Handler;
|
||||
|
||||
// Trace - 最详细的跟踪信息(灰色)
|
||||
logger.Trace("执行函数: UpdatePlayerPosition");
|
||||
|
||||
// Debug - 调试信息(青色)
|
||||
logger.Debug("当前帧率: {0}", Engine.GetFramesPerSecond());
|
||||
|
||||
// Info - 一般信息(白色)
|
||||
logger.Info("游戏开始");
|
||||
|
||||
// Warning - 警告信息(黄色)
|
||||
logger.Warn("资源加载缓慢: {0}ms", loadTime);
|
||||
|
||||
// Error - 错误信息(红色)
|
||||
logger.Error("无法加载配置文件");
|
||||
|
||||
// Fatal - 致命错误(红色,使用 PushError)
|
||||
logger.Fatal("游戏崩溃");
|
||||
RegisterHandler(new LoggingTransitionHandler());
|
||||
```
|
||||
|
||||
### 记录异常信息
|
||||
这也说明 Godot 日志页不需要重新定义一套“Godot 专用场景日志接口”;现有 Game 运行时日志在 Godot 宿主里本来就会复用
|
||||
这套 provider。
|
||||
|
||||
```csharp
|
||||
var logger = LoggerFactoryResolver.Provider.CreateLogger("SaveSystem");
|
||||
## Godot 控制台输出语义
|
||||
|
||||
try
|
||||
{
|
||||
SaveGame();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 记录异常信息
|
||||
logger.Error("保存游戏失败", ex);
|
||||
}
|
||||
当前 `GodotLogger.Write(...)` 的级别映射如下:
|
||||
|
||||
| 日志级别 | Godot 输出 API | 当前行为 |
|
||||
| --- | --- | --- |
|
||||
| `Trace` | `GD.PrintRich(...)` | 使用灰色富文本输出 |
|
||||
| `Debug` | `GD.PrintRich(...)` | 使用青色富文本输出 |
|
||||
| `Info` | `GD.Print(...)` | 普通控制台输出 |
|
||||
| `Warning` | `GD.PushWarning(...)` | 进入 Godot 警告通道 |
|
||||
| `Error` | `GD.PrintErr(...)` | 输出到错误流 |
|
||||
| `Fatal` | `GD.PushError(...)` | 进入 Godot 错误通道 |
|
||||
|
||||
异常追加格式也来自当前实现本身:
|
||||
|
||||
```text
|
||||
[2026-04-22 10:30:47.012] ERROR [SaveSystem] 保存游戏失败
|
||||
System.IO.IOException: ...
|
||||
```
|
||||
|
||||
## 高级用法
|
||||
如果你需要 JSON formatter、rolling file、namespace 级过滤、structured sink 组合,这已经超出
|
||||
`GFramework.Godot.Logging` 当前职责,应该回到 [Core 日志系统](../core/logging.md) 设计 provider 组合。
|
||||
|
||||
### 在 System 中使用日志
|
||||
## 什么时候用手写 logger,什么时候用 `[Log]`
|
||||
|
||||
```csharp
|
||||
using GFramework.Core.System;
|
||||
using GFramework.Core.Logging;
|
||||
using GFramework.Core.Abstractions.Logging;
|
||||
- 手写 `LoggerFactoryResolver.Provider.CreateLogger(...)`
|
||||
- 少量入口类
|
||||
- 需要自己控制字段名、静态/实例生命周期
|
||||
- 想明确看到 logger 初始化位置
|
||||
- 用 `[Log]`
|
||||
- Godot 节点、controller、system 上有大量重复 logger 字段样板
|
||||
- 你已经引用 `GFramework.Core.SourceGenerators`
|
||||
- 想把 logger 字段生成交给编译期
|
||||
|
||||
public class CombatSystem : AbstractSystem
|
||||
{
|
||||
private ILogger _logger;
|
||||
这里的边界要分清:
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
_logger = LoggerFactoryResolver.Provider.CreateLogger("CombatSystem");
|
||||
_logger.Info("战斗系统初始化完成");
|
||||
}
|
||||
- Godot provider:来自 `GFramework.Godot`
|
||||
- `[Log]` 生成器:来自 `GFramework.Core.SourceGenerators`
|
||||
|
||||
public void ProcessCombat(Entity attacker, Entity target, float damage)
|
||||
{
|
||||
_logger.Debug("战斗处理: {0} 攻击 {1}, 伤害: {2}",
|
||||
attacker.Name, target.Name, damage);
|
||||
它们是可组合关系,不是上下位替代关系。
|
||||
|
||||
if (damage > 100)
|
||||
{
|
||||
_logger.Warn("高伤害攻击: {0}", damage);
|
||||
}
|
||||
}
|
||||
## 当前边界
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
_logger.Info("战斗系统已销毁");
|
||||
}
|
||||
}
|
||||
```
|
||||
- 当前推荐接法是把 `GodotLoggerFactoryProvider` 放进 `ArchitectureConfiguration.LoggerProperties`;直接赋值
|
||||
`LoggerFactoryResolver.Provider` 仍然可用,但不该再写成默认采用路径
|
||||
- `GFramework.Godot.Logging` 只解决 Godot 控制台输出,不提供文件落盘、JSON formatter、异步 appender 或按 namespace
|
||||
的复杂过滤
|
||||
- `GodotLogger` 只改变输出方式,不改变 `ILogger` 接口本身;业务代码不需要切换到 Godot 专用日志 API
|
||||
- `[Log]`、`[ContextAware]` 这类字段注入能力不属于 `GFramework.Godot.Logging`
|
||||
- Scene / UI 的 `LoggingTransitionHandler` 位于 `GFramework.Game`,Godot 侧只是通过 provider 让它们输出到 Godot 控制台
|
||||
- 当前 `GodotLogger` 使用的是 UTC 时间戳;如果项目需要本地时区展示,需要自定义 provider / logger,而不是假定当前实现会自动转换
|
||||
|
||||
### 在 Model 中使用日志
|
||||
## 继续阅读
|
||||
|
||||
```csharp
|
||||
using GFramework.Core.Model;
|
||||
using GFramework.Core.Logging;
|
||||
using GFramework.Core.Abstractions.Logging;
|
||||
|
||||
public class PlayerModel : AbstractModel
|
||||
{
|
||||
private ILogger _logger;
|
||||
private int _health;
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
_logger = LoggerFactoryResolver.Provider.CreateLogger("PlayerModel");
|
||||
_logger.Info("玩家模型初始化");
|
||||
|
||||
_health = 100;
|
||||
}
|
||||
|
||||
public void SetHealth(int value)
|
||||
{
|
||||
var oldHealth = _health;
|
||||
_health = value;
|
||||
|
||||
_logger.Debug("玩家生命值变化: {0} -> {1}", oldHealth, _health);
|
||||
|
||||
if (_health <= 0)
|
||||
{
|
||||
_logger.Warn("玩家生命值归零");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 条件日志记录
|
||||
|
||||
```csharp
|
||||
var logger = LoggerFactoryResolver.Provider.CreateLogger("PerformanceMonitor");
|
||||
|
||||
// 检查日志级别是否启用,避免不必要的字符串格式化
|
||||
if (logger.IsDebugEnabled())
|
||||
{
|
||||
var stats = CalculateComplexStats(); // 耗时操作
|
||||
logger.Debug("性能统计: {0}", stats);
|
||||
}
|
||||
|
||||
// 简化写法
|
||||
if (logger.IsTraceEnabled())
|
||||
{
|
||||
logger.Trace("详细的执行流程信息");
|
||||
}
|
||||
```
|
||||
|
||||
### 分类日志记录
|
||||
|
||||
```csharp
|
||||
// 为不同模块创建独立的日志记录器
|
||||
var networkLogger = LoggerFactoryResolver.Provider.CreateLogger("Network");
|
||||
var databaseLogger = LoggerFactoryResolver.Provider.CreateLogger("Database");
|
||||
var aiLogger = LoggerFactoryResolver.Provider.CreateLogger("AI");
|
||||
|
||||
networkLogger.Info("连接到服务器");
|
||||
databaseLogger.Debug("查询用户数据");
|
||||
aiLogger.Trace("AI 决策树遍历");
|
||||
```
|
||||
|
||||
### 自定义日志级别
|
||||
|
||||
```csharp
|
||||
// 在开发环境使用 Debug 级别
|
||||
#if DEBUG
|
||||
LoggerFactoryResolver.Provider = new GodotLoggerFactoryProvider
|
||||
{
|
||||
MinLevel = LogLevel.Debug
|
||||
};
|
||||
#else
|
||||
// 在生产环境使用 Info 级别
|
||||
LoggerFactoryResolver.Provider = new GodotLoggerFactoryProvider
|
||||
{
|
||||
MinLevel = LogLevel.Info
|
||||
};
|
||||
#endif
|
||||
```
|
||||
|
||||
### 在 Godot 模块中使用日志
|
||||
|
||||
```csharp
|
||||
using GFramework.Godot.Architecture;
|
||||
using GFramework.Core.Logging;
|
||||
using GFramework.Core.Abstractions.Logging;
|
||||
using Godot;
|
||||
|
||||
public class SceneModule : AbstractGodotModule
|
||||
{
|
||||
private ILogger _logger;
|
||||
private Node _sceneRoot;
|
||||
|
||||
public override Node Node => _sceneRoot;
|
||||
|
||||
public SceneModule()
|
||||
{
|
||||
_sceneRoot = new Node { Name = "SceneRoot" };
|
||||
_logger = LoggerFactoryResolver.Provider.CreateLogger("SceneModule");
|
||||
}
|
||||
|
||||
public override void Install(IArchitecture architecture)
|
||||
{
|
||||
_logger.Info("场景模块安装开始");
|
||||
|
||||
// 安装场景系统
|
||||
var sceneSystem = new SceneSystem();
|
||||
architecture.RegisterSystem<ISceneSystem>(sceneSystem);
|
||||
|
||||
_logger.Info("场景模块安装完成");
|
||||
}
|
||||
|
||||
public override void OnPhase(ArchitecturePhase phase, IArchitecture architecture)
|
||||
{
|
||||
_logger.Debug("场景模块阶段: {0}", phase);
|
||||
|
||||
if (phase == ArchitecturePhase.Ready)
|
||||
{
|
||||
_logger.Info("场景模块已就绪");
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDetach()
|
||||
{
|
||||
_logger.Info("场景模块已分离");
|
||||
_sceneRoot?.QueueFree();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 日志输出格式
|
||||
|
||||
### 输出格式说明
|
||||
|
||||
Godot 日志系统使用以下格式输出日志:
|
||||
|
||||
```
|
||||
[时间戳] 日志级别 [日志器名称] 日志消息
|
||||
```
|
||||
|
||||
**示例输出**:
|
||||
|
||||
```
|
||||
[2025-01-09 10:30:45.123] INFO [GameArchitecture] 游戏架构初始化开始
|
||||
[2025-01-09 10:30:45.456] DEBUG [Player] 玩家位置: (100, 200)
|
||||
[2025-01-09 10:30:46.789] WARNING [CombatSystem] 高伤害攻击: 150
|
||||
[2025-01-09 10:30:47.012] ERROR [SaveSystem] 保存游戏失败
|
||||
```
|
||||
|
||||
### 日志级别与 Godot 输出方法
|
||||
|
||||
| 日志级别 | Godot 方法 | 颜色 | 说明 |
|
||||
|-------------|------------------|----|----------|
|
||||
| **Trace** | `GD.PrintRich` | 灰色 | 最详细的跟踪信息 |
|
||||
| **Debug** | `GD.PrintRich` | 青色 | 调试信息 |
|
||||
| **Info** | `GD.Print` | 白色 | 一般信息 |
|
||||
| **Warning** | `GD.PushWarning` | 黄色 | 警告信息 |
|
||||
| **Error** | `GD.PrintErr` | 红色 | 错误信息 |
|
||||
| **Fatal** | `GD.PushError` | 红色 | 致命错误 |
|
||||
|
||||
### 异常信息格式
|
||||
|
||||
当记录异常时,异常信息会附加到日志消息后:
|
||||
|
||||
```
|
||||
[2025-01-09 10:30:47.012] ERROR [SaveSystem] 保存游戏失败
|
||||
System.IO.IOException: 文件访问被拒绝
|
||||
at SaveSystem.SaveGame() in SaveSystem.cs:line 42
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **在架构初始化时配置日志系统**:
|
||||
```csharp
|
||||
public GameArchitecture()
|
||||
{
|
||||
LoggerFactoryResolver.Provider = new GodotLoggerFactoryProvider
|
||||
{
|
||||
MinLevel = LogLevel.Debug
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
2. **为每个类创建独立的日志记录器**:
|
||||
```csharp
|
||||
private ILogger _logger;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_logger = LoggerFactoryResolver.Provider.CreateLogger(GetType().Name);
|
||||
}
|
||||
```
|
||||
|
||||
3. **使用合适的日志级别**:
|
||||
- `Trace`:详细的执行流程,仅在深度调试时使用
|
||||
- `Debug`:调试信息,开发阶段使用
|
||||
- `Info`:重要的业务流程和状态变化
|
||||
- `Warning`:潜在问题但不影响功能
|
||||
- `Error`:错误但程序可以继续运行
|
||||
- `Fatal`:严重错误,程序无法继续
|
||||
|
||||
4. **检查日志级别避免性能损失**:
|
||||
```csharp
|
||||
if (_logger.IsDebugEnabled())
|
||||
{
|
||||
var expensiveData = CalculateExpensiveData();
|
||||
_logger.Debug("数据: {0}", expensiveData);
|
||||
}
|
||||
```
|
||||
|
||||
5. **提供有意义的上下文信息**:
|
||||
```csharp
|
||||
// ✗ 不好
|
||||
logger.Error("错误");
|
||||
|
||||
// ✓ 好
|
||||
logger.Error("加载场景失败: SceneKey={0}, Path={1}", sceneKey, scenePath);
|
||||
```
|
||||
|
||||
6. **记录异常时提供上下文**:
|
||||
```csharp
|
||||
try
|
||||
{
|
||||
LoadScene(sceneKey);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error($"加载场景失败: {sceneKey}", ex);
|
||||
}
|
||||
```
|
||||
|
||||
7. **使用分类日志记录器**:
|
||||
```csharp
|
||||
var networkLogger = LoggerFactoryResolver.Provider.CreateLogger("Network");
|
||||
var aiLogger = LoggerFactoryResolver.Provider.CreateLogger("AI");
|
||||
```
|
||||
|
||||
8. **在生命周期方法中记录关键事件**:
|
||||
```csharp
|
||||
protected override void OnInit()
|
||||
{
|
||||
_logger.Info("系统初始化完成");
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
_logger.Info("系统已销毁");
|
||||
}
|
||||
```
|
||||
|
||||
## 性能考虑
|
||||
|
||||
1. **日志缓存**:
|
||||
- `GodotLoggerFactoryProvider` 使用 `CachedLoggerFactory` 缓存日志记录器实例
|
||||
- 相同名称和级别的日志记录器会被复用
|
||||
|
||||
2. **级别检查**:
|
||||
- 日志方法会自动检查日志级别
|
||||
- 低于最小级别的日志不会被处理
|
||||
|
||||
3. **字符串格式化**:
|
||||
- 使用参数化日志避免不必要的字符串拼接
|
||||
```csharp
|
||||
// ✗ 不好 - 总是执行字符串拼接
|
||||
logger.Debug("位置: " + position.ToString());
|
||||
|
||||
// ✓ 好 - 只在 Debug 启用时格式化
|
||||
logger.Debug("位置: {0}", position);
|
||||
```
|
||||
|
||||
4. **条件日志**:
|
||||
- 对于耗时的数据计算,先检查日志级别
|
||||
```csharp
|
||||
if (logger.IsDebugEnabled())
|
||||
{
|
||||
var stats = CalculateComplexStats();
|
||||
logger.Debug("统计: {0}", stats);
|
||||
}
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 问题:如何配置 Godot 日志系统?
|
||||
|
||||
**解答**:
|
||||
在架构构造函数中配置日志提供程序:
|
||||
|
||||
```csharp
|
||||
public GameArchitecture()
|
||||
{
|
||||
LoggerFactoryResolver.Provider = new GodotLoggerFactoryProvider
|
||||
{
|
||||
MinLevel = LogLevel.Debug
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### 问题:日志没有输出到 Godot 控制台?
|
||||
|
||||
**解答**:
|
||||
检查以下几点:
|
||||
|
||||
1. 确认已配置 `GodotLoggerFactoryProvider`
|
||||
2. 检查日志级别是否低于最小级别
|
||||
3. 确认使用了正确的日志记录器
|
||||
|
||||
```csharp
|
||||
// 确认配置
|
||||
LoggerFactoryResolver.Provider = new GodotLoggerFactoryProvider
|
||||
{
|
||||
MinLevel = LogLevel.Trace // 设置为最低级别测试
|
||||
};
|
||||
|
||||
// 创建日志记录器
|
||||
var logger = LoggerFactoryResolver.Provider.CreateLogger("Test");
|
||||
logger.Info("测试日志"); // 应该能看到输出
|
||||
```
|
||||
|
||||
### 问题:如何在不同环境使用不同的日志级别?
|
||||
|
||||
**解答**:
|
||||
使用条件编译或环境检测:
|
||||
|
||||
```csharp
|
||||
public GameArchitecture()
|
||||
{
|
||||
var minLevel = OS.IsDebugBuild() ? LogLevel.Debug : LogLevel.Info;
|
||||
|
||||
LoggerFactoryResolver.Provider = new GodotLoggerFactoryProvider
|
||||
{
|
||||
MinLevel = minLevel
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### 问题:如何禁用某个模块的日志?
|
||||
|
||||
**解答**:
|
||||
为该模块创建一个高级别的日志记录器:
|
||||
|
||||
```csharp
|
||||
// 只记录 Error 及以上级别
|
||||
var logger = new GodotLogger("VerboseModule", LogLevel.Error);
|
||||
```
|
||||
|
||||
### 问题:日志输出影响性能怎么办?
|
||||
|
||||
**解答**:
|
||||
|
||||
1. 提高最小日志级别
|
||||
2. 使用条件日志
|
||||
3. 避免在高频调用的方法中记录日志
|
||||
|
||||
```csharp
|
||||
// 提高日志级别
|
||||
LoggerFactoryResolver.Provider = new GodotLoggerFactoryProvider
|
||||
{
|
||||
MinLevel = LogLevel.Warning // 只记录警告及以上
|
||||
};
|
||||
|
||||
// 使用条件日志
|
||||
if (_logger.IsDebugEnabled())
|
||||
{
|
||||
_logger.Debug("高频数据: {0}", data);
|
||||
}
|
||||
|
||||
// 避免在 _Process 中频繁记录
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
// ✗ 不好 - 每帧都记录
|
||||
// _logger.Debug("帧更新");
|
||||
|
||||
// ✓ 好 - 只在特定条件下记录
|
||||
if (someErrorCondition)
|
||||
{
|
||||
_logger.Error("检测到错误");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 问题:如何记录结构化日志?
|
||||
|
||||
**解答**:
|
||||
使用参数化日志或 `IStructuredLogger` 接口:
|
||||
|
||||
```csharp
|
||||
// 参数化日志
|
||||
logger.Info("玩家登录: UserId={0}, UserName={1}, Level={2}",
|
||||
userId, userName, level);
|
||||
|
||||
// 使用结构化日志(如果实现了 IStructuredLogger)
|
||||
if (logger is IStructuredLogger structuredLogger)
|
||||
{
|
||||
structuredLogger.Log(LogLevel.Info, "玩家登录",
|
||||
("UserId", userId),
|
||||
("UserName", userName),
|
||||
("Level", level));
|
||||
}
|
||||
```
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [核心日志系统](/zh-CN/core/logging) - GFramework 核心日志功能
|
||||
- [Godot 架构集成](/zh-CN/godot/architecture) - Godot 架构系统
|
||||
- [Godot 扩展](/zh-CN/godot/extensions) - Godot 扩展方法
|
||||
- [最佳实践](/zh-CN/best-practices/architecture-patterns) - 架构最佳实践
|
||||
- [Core 日志系统](../core/logging.md)
|
||||
- [日志生成器](../source-generators/logging-generator.md)
|
||||
- [Godot 运行时集成](./index.md)
|
||||
- [Godot 场景系统](./scene.md)
|
||||
- [Godot UI 系统](./ui.md)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user