Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
日志
Godot 提供了多种方式来组织和收集日志消息。
打印消息
项目设置
Godot 中有几个项目设置可以用来控制日志记录(打印输出)的行为:
应用程序 > 运行 > 禁用标准输出(Application > Run > Disable stdout): 完全禁用向标准输出的日志记录。这也会影响自定义日志记录器接收到的内容。可在运行时通过设置 Engine.print_to_stdout 来控制此选项。
应用程序 > 运行 > 禁用标准错误(Application > Run > Disable stderr): 完全禁用向标准错误输出的日志记录。这也会影响自定义日志记录器接收到的内容。可在运行时通过设置 Engine.print_error_messages 来控制此选项。
调试 > 设置 > 标准输出 > 详细标准输出(Debug > Settings > stdout > Verbose stdout): 启用向标准输出的详细日志记录。仅当启用详细模式时,print_verbose() 的输出内容才会显示。
调试 > 设置 > 标准输出 > 打印 FPS(Debug > Settings > stdout > Print FPS): 每秒打印一次帧率(FPS),并在启动时打印垂直同步(V-Sync)状态(因为它可能有效限制最大帧率)。
调试 > 设置 > 标准输出 > 打印 GPU 性能分析(Debug > Settings > stdout > Print GPU Profile): 每秒打印一次 GPU 使用情况报告,数据来源与 可视分析器 相同。
这些项目设置中的一部分也可以通过 command line arguments 进行覆盖,例如 --quiet(静默模式)、--verbose(详细模式)和 --print-fps(打印帧率)。
引擎自身的文件日志记录功能也可进行配置,具体说明见下文。
内置文件日志记录
在桌面平台上,Godot 默认会将日志文件写入 user://logs/godot.log 。你可以通过修改 debug/file_logging/log_path 项目设置来更改这个存储位置。日志文件会进行轮转(rotate),以便保留旧文件供你排查问题。每次运行会话都会创建一个全新的日志文件,而旧的日志文件会被重命名,并在文件名中附带它被轮转时的日期。默认情况下,Godot 会保留最多 5 个日志文件,你可以通过 debug/file_logging/max_log_files 项目设置来调整这个数量。
你也可以通过修改 debug/file_logging/enable_file_logging 这个项目设置,来彻底关闭文件日志记录功能。
当项目发生崩溃时,崩溃日志会被写入到和常规日志相同的文件中。不过,只有当运行的程序(二进制文件)本身包含调试符号(debugging symbols),或者它能找到与该程序匹配的调试符号文件时,崩溃日志里才会包含真正有用的回溯信息(backtrace)。官方提供的程序是不带调试符号的,所以如果你想获取有效的崩溃回溯信息,就需要自己进行自定义编译。关于如何编译启用调试符号的程序,具体可以查阅:参考 Debugging symbols 部分的相关指南。
备注
print() 语句生成的日志文件,只有当引擎对标准输出(standard output)进行 刷新(flush) 操作时才会更新。在调试版(debug builds)中,每次执行打印操作时都会刷新标准输出。而在以发布模式(release mode)导出的项目中,为了提升性能,标准输出只有在项目退出或崩溃时才会被刷新——尤其是当项目需要频繁向标准输出打印文本时,这种机制能显著减少性能损耗。
另一方面,标准错误流(由 printerr() 、 push_error() 和 push_warning() 使用)会在每次打印时立即强制刷新,即使是在以发布(release)模式导出的项目中也是如此。
对于某些特定的使用场景(比如搭建专用服务器),大家可能更倾向于让发布版(release builds)在每次执行打印操作时都强制刷新标准输出。这样一来,像 journald 这样的日志服务就能在程序进程运行的过程中,实时收集到日志信息了。这可以通过在项目设置中启用 application/run/flush_stdout_on_print 来实现。
脚本调用栈
自 Godot 4.5 起,当 GDScript 代码遇到错误时,会记录一个指向错误源头的回溯信息(backtrace),并包含导致该错误的调用栈。此功能在编辑器内运行或项目以调试模式导出时始终启用。
在以发布模式导出的项目中,出于性能考虑,默认禁用回溯功能。您可以通过在项目设置中勾选 调试 > 设置 > GDScript > 始终跟踪调用栈(Debug > Settings > GDScript > Always Track Call Stacks) 来启用该功能。如果您使用自定义日志系统将异常上报至远程服务,建议启用此选项,以便让上报的错误信息更具可操作性。
崩溃回溯
警告
崩溃回溯仅在包含 debugging symbols 的构建版本中才有实际意义。官方发布的 Godot 二进制文件不包含调试符号,因此您必须自行编译自定义的编辑器或导出模板二进制文件,才能获得有用的崩溃回溯信息。
当项目崩溃时,崩溃回溯信息会被打印到标准错误流。在包含调试符号的构建版本中,其输出可能如下所示:
================================================================
handle_crash: Program crashed with signal 4
Engine version: Godot Engine v4.5.beta.custom_build (6c9aa4c7d3b9b91cd50714c40eeb234874df7075)
Dumping the backtrace. Please include this when reporting the bug to the project developer.
[1] /lib64/libc.so.6(+0x1a070) [0x7f6e5e277070] (??:0)
[2] godot() [0x4da3358] (/path/to/godot/core/core_bind.cpp:336 (discriminator 2))
[3] godot() [0xdf5f2f] (/path/to/godot/modules/gdscript/gdscript.h:591)
[4] godot() [0xbffd46] (/path/to/godot/modules/gdscript/gdscript.cpp:2065 (discriminator 1))
[5] godot() [0x30f2ea4] (/path/to/godot/core/variant/variant.h:870)
[6] godot() [0x550d4e1] (/path/to/godot/core/object/object.cpp:933)
[7] godot() [0x30d996a] (/path/to/godot/scene/main/node.cpp:318 (discriminator 1))
[8] godot() [0x3131a7f] (/path/to/godot/core/templates/hash_map.h:465)
[9] godot() [0x424589] (/path/to/godot/platform/linuxbsd/os_linuxbsd.cpp:970)
[10] /lib64/libc.so.6(+0x3575) [0x7f6e5e260575] (??:0)
[11] /lib64/libc.so.6(__libc_start_main+0x88) [0x7f6e5e260628] (??:0)
[12] godot() [0x464df5] (??:?)
-- END OF C++ BACKTRACE --
================================================================
GDScript backtrace (most recent call first):
[0] _ready (res://test.gd:5)
-- END OF GDSCRIPT BACKTRACE --
================================================================
另一方面,如果没有调试符号,输出将如下所示:
================================================================
handle_crash: Program crashed with signal 4
Engine version: Godot Engine v4.5.beta.custom_build (6c9aa4c7d3b9b91cd50714c40eeb234874df7075)
Dumping the backtrace. Please include this when reporting the bug to the project developer.
[1] /lib64/libc.so.6(+0x1a070) [0x7fdfaf666070] (??:0)
[2] godot() [0x4da3358] (??:0)
[3] godot() [0xdf5f2f] (??:0)
[4] godot() [0xbffd46] (??:0)
[5] godot() [0x30f2ea4] (??:0)
[6] godot() [0x550d4e1] (??:0)
[7] godot() [0x30d996a] (??:0)
[8] godot() [0x3131a7f] (??:0)
[9] godot() [0x424589] (??:0)
[10] /lib64/libc.so.6(+0x3575) [0x7fdfaf64f575] (??:0)
[11] /lib64/libc.so.6(__libc_start_main+0x88) [0x7fdfaf64f628] (??:0)
[12] godot() [0x464df5] (??:0)
-- END OF C++ BACKTRACE --
================================================================
GDScript backtrace (most recent call first):
[0] _ready (res://test.gd:5)
-- END OF GDSCRIPT BACKTRACE --
================================================================
此回溯信息也会被记录到当前会话的日志文件中,但 not 显示在编辑器的输出面板中。由于引擎崩溃时脚本系统已停止运行,因此无法在同一次会话中通过脚本访问该信息。不过,您仍可在下一次会话中通过加载日志文件,并使用 FileAccess 搜索崩溃回溯字符串(Program crashed with signal)来读取崩溃回溯内容。只要用户重新启动项目且已启用文件日志记录,即可在崩溃后获取回溯信息:
# This script can be made an autoload, so that it runs when the project starts.
extends Node
func _ready() -> void:
var log_dir: String = String(ProjectSettings.get_setting("debug/file_logging/log_path")).get_base_dir()
# Get the last log file by alphabetical order.
# Since the timestamp is featured in the file name, it should always be the most recent
# log file that was rotated. The non-timestamped log file is for the current session,
# so we don't want to read that one.
var last_log_file: String = log_dir.path_join(DirAccess.get_files_at(log_dir)[-1])
var last_long_contents: String = FileAccess.get_file_as_string(last_log_file)
var crash_begin_idx: int = last_long_contents.find("Program crashed with signal")
if crash_begin_idx != -1:
print("The previous session has crashed with the following backtrace:\n")
print(last_long_contents.substr(crash_begin_idx))
您可以通过 调试 > 设置 > 崩溃处理器 > 消息(Debug > Settings > Crash Handler > Message) 项目设置,自定义回溯信息顶部显示的消息内容。此功能可用于引导用户访问指定的网址或邮箱地址,以便提交问题反馈。
创建自定义日志记录器
自 Godot 4.5 起,可以创建自定义日志记录器。自定义日志功能可用于多种用途:
在游戏内显示一个控制台,展示与引擎输出相同的消息,而无需修改其他脚本。
将玩家设备上打印的错误信息上报至远程服务器。这有助于开发者在游戏已发布或进行试玩测试时更便捷地修复错误。
将专用服务器导出版本与监控平台集成。
可通过创建一个继承自 Logger 的类来注册自定义日志记录器,然后在脚本的 OS.add_logger 方法中,将该类的实例传递给 _init()。一个合适的实现位置是 autoload。
该类必须定义两个方法:_log_message() 和 _log_error()。
以下是一个最小可运行的自定义日志记录器示例,该脚本已作为自动加载(autoload)添加:
extends Node
class CustomLogger extends Logger:
# Note that this method is not called for messages that use
# `push_error()` and `push_warning()`, even though these are printed to stderr.
func _log_message(message: String, error: bool) -> void:
# Do something with `message`.
# `error` is `true` for messages printed to the standard error stream (stderr) with `print_error()`.
# Note that this method will be called from threads other than the main thread, possibly at the same
# time, so you will need to have some kind of thread-safety as part of it, like a Mutex.
pass
func _log_error(
function: String,
file: String,
line: int,
code: String,
rationale: String,
editor_notify: bool,
error_type: int,
script_backtraces: Array[ScriptBacktrace]
) -> void:
# Do something with the error. The error text is in `rationale`.
# See the Logger class reference for details on other parameters.
# Note that this method will be called from threads other than the main thread, possibly at the same
# time, so you will need to have some kind of thread-safety as part of it, like a Mutex.
pass
# Use `_init()` to initialize the logger as early as possible, which ensures that messages
# printed early are taken into account. However, even when using `_init()`, the engine's own
# initialization messages are not accessible.
func _init() -> void:
OS.add_logger(CustomLogger.new())
请注意,为避免无限递归,在 _log_message() 中不能有效使用 print() 及其相关方法;同样,在 _log_error() 中也不能有效使用 push_error() 或 push_warning()。若尝试这样做,消息将被输出到与原始消息相同的流中,而该消息对自定义日志记录器不可见,这正是防止无限递归发生的原因:
While attempting to print a message, another message was printed:
...
While attempting to print an error, another error was printed:
...
参见
你可以在 自定义日志记录演示项目 中,找到一个使用自定义日志记录器构建的游戏内控制台(in-game console)的示例。