Files
XCEngine/docs/issues/Editor模块_Console面板错误绑定fallback sink导致运行时日志不显示4.3.md

180 lines
5.0 KiB
Markdown
Raw Permalink Normal View History

2026-04-08 16:07:03 +08:00
# Editor模块 Console面板错误绑定 fallback sink 导致运行时日志不显示
## 1. 问题定义
当前 editor 底部 `Console` 面板在 PlayMode 下看不到 C# `Debug.Log` 输出。
已确认:
1. `Tick` 正在正常推进,脚本字段计数持续变化
2. `Debug.Log` 已经成功从 C# 进入 native logger
3. `editor/bin/Debug/editor.log` 中可以看到脚本日志
4. 但 editor 底部 `Console` 面板仍然为空,或看不到对应运行时日志
这说明问题不在脚本运行时本身,而在 editor 控制台面板读取日志数据的链路。
---
## 2. 复现现象
复现方式:
1. 给场景对象挂上 `project/Assets/Scripts/TickLogProbe.cs`
2. 点击 editor 的 `Play`
3. 观察脚本上的计数字段持续递增
4. 观察底部 `Console` 面板,没有出现 `[Project TickLogProbe] Awake/Start/Update/...`
同时检查:
- `editor/bin/Debug/editor.log`
可以看到类似日志已经真实写入:
- `[INFO] [Scripting] [Project TickLogProbe] Awake`
- `[INFO] [Scripting] [Project TickLogProbe] Start`
- `[INFO] [Scripting] [Project TickLogProbe] Update 1`
---
## 3. 排查结论
问题不在以下层级:
1. 不在 `Tick` 系统
2. 不在 C# `Debug.Log`
3. 不在 Mono internal call 注册
4. 不在 native `Logger`
5. 不在 `FileLogSink`
这些链路都已经工作正常。
真正出问题的是:
- editor `ConsolePanel` 读到的不是 logger 中真实注册的 `EditorConsoleSink`
- 它读到的是一个错误的 fallback 实例
---
## 4. 根本原因
根因位于:
- `editor/src/Core/EditorConsoleSink.cpp`
当前实现:
1. `ConfigureEditorLogging()` 启动时会把一个真正的 `EditorConsoleSink` 注册到 `Logger`
2. 后续 `ConsolePanel::OnAttach()` 会调用 `EditorConsoleSink::GetInstance()`
3. `GetInstance()` 内部存在一个 `static EditorConsoleSink fallbackInstance`
4.`EditorConsoleSink` 的构造函数会无条件执行 `s_instance = this`
这会导致一个严重副作用:
1. logger 里原本已经注册好的真实 `EditorConsoleSink` 先成为 `s_instance`
2.`ConsolePanel` 第一次调用 `GetInstance()` 时,`fallbackInstance` 被构造
3. `fallbackInstance` 构造时又把 `s_instance` 覆盖成它自己
4. 此后 `ConsolePanel` 通过 `GetInstance()` 拿到的是 fallback sink
5. 但 logger 继续写入的仍然是最初注册到 `Logger` 的那一个真实 sink
最终结果就是:
1. 文件日志正常
2. logger 正常
3. 脚本日志正常
4. Console 面板读取的是另一份空数据
---
## 5. 关键代码位置
相关位置:
- `editor/src/Core/EditorLoggingSetup.h`
- `editor/src/Core/EditorConsoleSink.cpp`
- `editor/src/panels/ConsolePanel.cpp`
- `editor/src/Application.cpp`
关键调用顺序:
1. `Application::Initialize()`
2. `ConfigureEditorLogging()`
3. `Logger` 注册真实 `EditorConsoleSink`
4. `AttachEditorLayer()`
5. `ConsolePanel::OnAttach()`
6. `EditorConsoleSink::GetInstance()`
7. fallback 实例构造并错误覆盖 `s_instance`
---
## 6. 影响范围
这个问题的影响不只是 `TickLogProbe` 看不到日志,而是整个 editor runtime 日志可视化都不可信:
1. 任何 C# `Debug.Log`
2. 任何 `Scripting` 分类日志
3. 未来 PlayMode 运行时诊断
4. 运行时错误追踪
5. 面向脚本模块的调试体验
从开发效率上看,这个问题会直接误导判断:
1. 容易误以为脚本没执行
2. 容易误以为 `Debug.Log` 没打通
3. 容易误以为 Tick 没跑
4. 实际上只是 Console 面板读错了 sink
---
## 7. 修复方向
建议修复方向:
1. `EditorConsoleSink::GetInstance()` 不应在查询过程中构造会修改全局状态的 fallback 对象
2. `EditorConsoleSink` 构造函数不应无条件覆盖 `s_instance`
3. 应保证 `ConsolePanel` 永远读取 logger 中真实注册的那一个 sink
4. 如果确实需要 fallback也应是只读占位对象不能污染全局 `s_instance`
更稳妥的方向包括:
1. 明确区分“真实注册实例”和“空对象返回值”
2.`ConfigureEditorLogging()` 的注册实例成为唯一权威实例
3. 增加 editor 级回归测试覆盖“logger 已写入但 ConsolePanel 读取为空”的场景
---
## 8. 验收标准
修复后至少应满足:
1. `Debug.Log` 能实时显示在 editor 底部 `Console` 面板
2. `editor.log``Console` 面板看到的是同一批日志
3. `ConsolePanel` 读取到的 `EditorConsoleSink` 与 logger 注册实例一致
4. PlayMode 期间 `Awake/Start/FixedUpdate/Update/LateUpdate` 日志都可见
5. 不再出现“文件里有日志但面板里没有”的分裂状态
---
## 9. 优先级
高。
原因:
1. 当前脚本运行时其实已经可用
2. 但最基础的可视化调试入口失效
3. 会直接阻塞后续对脚本生命周期、PlayMode 行为、运行时异常的验证效率
---
## 10. 备注
本问题已经确认不是以下原因导致:
1. `Tick` 没运行
2. `Script` 没挂载成功
3. `Debug.Log` internal call 没注册
4. logger category 被禁用
5. `editor.log` 没写入
这是一个纯 editor 层的 Console sink 实例绑定错误问题。