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.

記錄

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() 的輸出僅在啟用詳細模式時可見。

  • Debug > Settings > stdout > Print FPS: 每秒列印每秒幀數, 並在啟動時列印 V-Sync 狀態 (因為它會有效限制最大幀率)。

  • Debug > Settings > stdout > Print GPU Profile: 每秒列印 GPU 使用狀況報告, 資料來源與 視覺化分析 相同。

其中一些專案設定也可透過 指令列參數 覆寫,例如 --quiet--verbose--print-fps

引擎內建的檔案日誌也可設定,如下節所述。

內建檔案日誌

預設情況下,Godot 會在桌面平台的 user://logs/godot.log 寫入日誌檔。你可透過修改專案設定 debug/file_logging/log_path 來變更此位置。為了保留舊檔供檢視,日誌會進行輪替。每次執行都會建立新的日誌檔,舊檔則會重新命名以包含被輪替時的日期。預設會保留最多 5 個日誌檔,可透過 debug/file_logging/max_log_files 調整。

也可透過 debug/file_logging/enable_file_logging 完全停用檔案日誌。

當專案崩潰時,崩潰日誌會寫入與一般日誌相同的檔案。只有在執行的二進位包含除錯符號,或能找到與之相符的除錯符號檔時,崩潰日誌才會包含可用的回溯。官方二進位不提供除錯符號,因此需要自訂建置才能生效。請見 除錯符號 以了解如何在啟用除錯符號的情況下編譯二進位。

備註

針對 print() 所列印的訊息, 日誌檔會在引擎將標準輸出 flush 時更新。僅在除錯建置中, 標準輸出會於每次列印時進行 flush。在以發行模式匯出的專案中, 標準輸出只會在專案結束或崩潰時才進行 flush, 以提升效能, 特別是在專案經常列印文字到標準輸出的情況下。

相對地,標準錯誤串流(由 printerr()push_error()push_warning() 使用)在每次列印時都會進行沖洗,即使在以發行模式匯出的專案也是如此。

在某些情境(如專用伺服器)下,可能希望發行建置在列印時也總是沖洗標準輸出,讓 journald 等日誌服務能在行程執行期間收集日誌。你可以在專案設定中啟用 application/run/flush_stdout_on_print 來達成。

腳本回溯

自 Godot 4.5 起,當 GDScript 程式碼遇到錯誤時,會記錄一份回溯,指出錯誤的來源,並包含導致該錯誤的呼叫堆疊。此行為在編輯器中執行或以除錯模式匯出專案時一律啟用。

在以發行模式匯出的專案中,為了效能,預設會停用回溯。你可以在專案設定中勾選 Debug > Settings > GDScript > Always Track Call Stacks 來啟用它。若你使用自訂的日誌系統將例外回報到遠端服務,建議啟用此選項以提升錯誤的可處理性。

崩潰回溯

警告

崩潰回溯只有在以包含 除錯符號 的建置記錄時才有用。官方 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 --
================================================================

這份回溯也會記錄到目前工作階段的日誌檔中, 但它在編輯器的 Output 面板中是看 到的。由於引擎在崩潰時, 其腳本系統已不再執行, 因此無法在同一工作階段中以腳本方式存取。不過, 你仍可在下一次啟動時讀取日誌檔, 並使用 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 自訂回溯頂端顯示的訊息。這可用來提供回報問題的 URL 或電子郵件地址。

建立自訂記錄器

自 Godot 4.5 起,可以建立自訂記錄器。這類自訂記錄可用於多種目的:

  • 顯示遊戲內主控台,與引擎所列印的訊息保持一致,而無需修改其他腳本。

  • 將玩家機器上列印出的錯誤回報到遠端伺服器,讓開發者在遊戲已發行或測試期間更容易修復問題。

  • 將專用伺服器的匯出版本與監控平台整合。

要註冊自訂記錄器,先建立一個繼承自 Logger 的類別,然後在腳本的 _init() 方法中,將其實例傳給 OS.add_logger。不錯的放置地點是 自動載入

該類別必須定義兩個方法:_log_message()_log_error()

以下是一個可運作的最小自訂記錄器範例,並將腳本加入為自動載入:

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:
...

也參考

你可以在 自訂記錄範例專案 中找到一個以自訂記錄器製作的遊戲內主控台範例。