180 lines
5.0 KiB
Markdown
180 lines
5.0 KiB
Markdown
|
|
# 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 实例绑定错误问题。
|