記錄

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

也參考

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