Files
XCEngine/docs/api/_guides/Debug/Logging-Architecture.md

108 lines
3.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Logging Architecture
## 这套日志系统解决什么问题
在游戏引擎里,日志不是单纯的输出文本,而是跨模块诊断链路。渲染后端、资源系统、编辑器工具和自动化测试都需要记录问题,但它们不应该各自决定“写到哪里、以什么格式写、是否需要同时写文件和控制台”。
`XCEngine::Debug::Logger` 采用中心分发 + sink 的结构,就是为了把这两类职责拆开:
- 业务层负责说明“发生了什么”。
- `Logger` 负责说明“这条记录如何统一过滤和分发”。
- sink 负责说明“最终落到哪里”。
## 为什么不是 everywhere `printf`
直接 `printf` 的问题是:
- 输出目的地写死在调用点里。
- 无法统一按级别和分类过滤。
- 很难同时输出到控制台、文件和编辑器 UI。
- 工具层很难在不修改业务代码的前提下插入额外日志通道。
sink 模式的好处是,业务代码只需要依赖:
```cpp
Logger::Get().Info(LogCategory::General, "...");
```
至于这条日志最终是进入控制台、文件还是编辑器面板,可以由启动阶段自由拼装。
## 当前架构中的角色分工
### `Logger`
- 维护全局最小级别。
- 维护 category 开关。
- 生成 `LogEntry`
- 把日志广播给已注册 sink。
### `ILogSink`
- 定义输出目标的最小契约。
- 允许扩展出控制台、文件、内存缓存或远程诊断通道。
### `ConsoleLogSink`
- 面向“立即可见”的输出。
- 最适合测试程序、命令行工具和开发期启动日志。
### `FileLogSink`
- 面向“事后复盘”的输出。
- 最适合编辑器、长时间运行工具和自动化测试。
## 推荐接线方式
启动阶段推荐做三件事:
1. 显式 `Logger::Get().Initialize()`
2. 注册至少一个可见 sink例如 `ConsoleLogSink`
3. 如果程序有会话级价值,再追加 `FileLogSink`
示例:
```cpp
using namespace XCEngine::Debug;
Logger::Get().Initialize();
Logger::Get().AddSink(std::make_unique<ConsoleLogSink>());
Logger::Get().AddSink(std::make_unique<FileLogSink>("editor.log"));
Logger::Get().SetMinimumLevel(LogLevel::Debug);
```
这也是当前编辑器和多个集成测试采用的基本模式。
## 和 Unity 的对照
可以把这套结构理解为:
- 比 Unity Console 更底层,因为它先解决日志分发,再决定是否显示在 UI。
- 比单纯的 `Debug.Log` 更偏引擎层,因为它允许接多个输出目标。
但它又明显没有 Unity 那么完整:
- 还没有统一的上下文对象、堆栈追踪或富文本格式化。
- `XE_ASSERT` 当前也只会记一条 `Fatal` 日志,并不会中断执行。
## 当前版本最重要的限制
- `Logger::SetMinimumLevel``SetCategoryEnabled` 当前没有加锁,最好在初始化阶段配置。
- `Shutdown` 不应和其它线程的 `Log` 并发执行。
- 便捷方法 `Info``Warning` 等默认不带源码位置信息;需要文件和行号时,应优先使用 `XE_LOG`
- `FileLogSink` 会每次写入后立刻刷新文件,诊断友好,但吞吐量不是最优。
## 推荐实践
- 引擎启动、设备创建、资源加载失败这类关键路径,用 `Info` / `Warning` / `Error` 做结构化记录。
- 高频噪声日志放到 `Verbose``Debug`,并通过最小级别统一控制。
- category 要按子系统区分,不要把所有信息都塞到 `General`
- 如果未来需要编辑器控制台过滤、远程日志或测试断言统计,优先加 sink不要让业务代码直接分叉输出逻辑。
## 相关 API
- [Debug](../../XCEngine/Debug/Debug.md)
- [Logger](../../XCEngine/Debug/Logger/Logger.md)
- [ILogSink](../../XCEngine/Debug/ILogSink/ILogSink.md)
- [ConsoleLogSink](../../XCEngine/Debug/ConsoleLogSink/ConsoleLogSink.md)
- [FileLogSink](../../XCEngine/Debug/FileLogSink/FileLogSink.md)