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

5.0 KiB
Raw Permalink Blame History

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.logConsole 面板看到的是同一批日志
  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 实例绑定错误问题。