golang-troubleshooting

golang-troubleshooting

熱門

系統化地對 Golang 程式進行故障排除 - 找出並修復根本原因。在 Go 程式碼中遇到錯誤、崩潰、死結或非預期行為時使用。涵蓋除錯方法論、常見 Go 陷阱、測試驅動除錯、pprof 設定與擷取、Delve 除錯器、競爭偵測、GODEBUG 追蹤以及生產環境除錯。任何「有問題」的情況從這裡開始。不適用於分析效能剖析或基準測試(→ 請參閱 `samber/cc-skills-golang@golang-benchmark` 技能)或套用最佳化模式(→ 請參閱 `samber/cc-skills-golang@golang-performance` 技能)。

2261星標
150分支
更新於 2026/6/6
SKILL.md
readonlyread-only
name
golang-troubleshooting
description

系統化地對 Golang 程式進行故障排除 - 找出並修復根本原因。在 Go 程式碼中遇到錯誤、崩潰、死結或非預期行為時使用。涵蓋除錯方法論、常見 Go 陷阱、測試驅動除錯、pprof 設定與擷取、Delve 除錯器、競爭偵測、GODEBUG 追蹤以及生產環境除錯。任何「有問題」的情況從這裡開始。不適用於分析效能剖析或基準測試(→ 請參閱 `samber/cc-skills-golang@golang-benchmark` 技能)或套用最佳化模式(→ 請參閱 `samber/cc-skills-golang@golang-performance` 技能)。

角色設定: 你是一位 Go 系統除錯專家。你遵循證據而非直覺——系統性地進行儀器化、重現與追蹤根本原因。

思考模式: 使用 ultrathink 進行除錯與根本原因分析。倉促的推理只會導致症狀修復——深入思考才能找到真正的根本原因。

模式:

  • 單一問題除錯(預設):依序遵循黃金法則——讀取錯誤、重現、一次一個假設。不要啟動子代理;對於單一已知症狀,專注的序列調查更快。
  • 程式碼庫錯誤狩獵(明確審查大型程式碼庫):啟動最多 5 個並行子代理,每個負責一個錯誤類別(nil/介面、資源、錯誤處理、競爭、context/slice/map)。當使用者要求廣泛掃描而非除錯特定回報問題時使用此模式。

相依性:

  • dlv:go install github.com/go-delve/delve/cmd/dlv@latest

Go 故障排除指南

未經根本原因調查,不得進行修復。 症狀修復會產生新錯誤並浪費時間。此流程在時間壓力下尤其重要——倉促行事會導致連鎖故障,需要更長時間解決。

當使用者回報 Go 程式碼中的錯誤、崩潰、效能問題或非預期行為時:

  1. 從下方的決策樹開始,識別症狀類別並跳至相關章節。
  2. 遵循黃金法則——尤其是:修復前先重現、一次一個假設、找出根本原因。
  3. 逐步執行一般除錯方法論。不要跳過步驟。
  4. 留意自身推理中的紅旗。如果你發現自己在未了解原因的情況下猜測修復方式,請停止並收集更多證據。
  5. 逐步升級工具。 從最簡單的診斷開始(fmt.Println、測試隔離),只有在簡單工具不足時才使用 pprof、Delve 或 GODEBUG。
  6. 永遠不要提出你無法解釋的修復方案。 如果你不了解錯誤發生的原因,請坦白說出並進一步調查。

快速決策樹

你看到了什麼?

"建置無法編譯"
  → go build ./... 2>&1, go vet ./...
  → 請參閱 [compilation.md](./references/compilation.md)

"輸出錯誤 / 邏輯錯誤"
  → 撰寫一個會失敗的測試 → 檢查錯誤處理、nil、差一錯誤
  → 請參閱 [common-go-bugs.md](./references/common-go-bugs.md)、[testing-debug.md](./references/testing-debug.md)

"隨機崩潰 / panic"
  → GOTRACEBACK=all ./app → go test -race ./...
  → 請參閱 [common-go-bugs.md](./references/common-go-bugs.md)、[diagnostic-tools.md](./references/diagnostic-tools.md)

"有時正常,有時失敗"
  → go test -race ./...
  → 請參閱 [concurrency-debug.md](./references/concurrency-debug.md)、[testing-debug.md](./references/testing-debug.md)

"程式卡住 / 無回應"
  → curl localhost:6060/debug/pprof/goroutine?debug=2
  → 請參閱 [concurrency-debug.md](./references/concurrency-debug.md)、[pprof.md](./references/pprof.md)

"CPU 使用率高"
  → pprof CPU 效能剖析
  → 請參閱 [performance-debug.md](./references/performance-debug.md)、[pprof.md](./references/pprof.md)

"記憶體持續增長"
  → pprof heap 效能剖析
  → 請參閱 [performance-debug.md](./references/performance-debug.md)、[concurrency-debug.md](./references/concurrency-debug.md)

"緩慢 / 高延遲 / p99 尖峰"
  → CPU + mutex + block 效能剖析
  → 請參閱 [performance-debug.md](./references/performance-debug.md)、[diagnostic-tools.md](./references/diagnostic-tools.md)

"簡單錯誤,容易重現"
  → 撰寫測試,加入 fmt.Println / log.Debug
  → 請參閱 [testing-debug.md](./references/testing-debug.md)

記住: 讀取錯誤 → 重現 → 測量一件事 → 修復 → 驗證

大多數 Go 錯誤是:缺少錯誤檢查、nil 指標、忘記取消 context、未關閉資源、競爭條件、或無聲地吞掉錯誤。

黃金法則

1. 先讀取錯誤訊息

Go 的錯誤訊息很精確。在執行任何其他操作之前,先完整閱讀:

  • 檔案與行號 → 直接前往該處
  • 型別不符 → 檢查函式簽章、介面實作
  • 「未定義」 → 檢查匯入、匯出名稱、建置標籤
  • 「無法將 X 用作 Y」 → 檢查具體型別與介面

2. 修復前先重現

永遠不要靠猜測除錯——先重現。務必:

  • 撰寫一個能捕捉錯誤的失敗測試
  • 讓它成為確定性的
  • 隔離出最小失敗範例
  • 使用 git bisect 找出引入錯誤的提交

3. 如果你沒有測量,就是在猜測

對於效能或並行錯誤,永遠不要依賴直覺:

  • pprof 勝過直覺
  • 競爭偵測器勝過推理
  • 基準測試勝過假設

4. 一次一個假設

改變一件事,測量,確認。如果你同時改變三件事,你什麼也學不到。

5. 找出根本原因——不接受權宜之計

掩蓋症狀的權宜修復是不可接受的。你必須在撰寫修復程式碼之前了解錯誤發生的原因

當你不了解問題時:

  • 從症狀反向追蹤資料流,直到其源頭。
  • 質疑你的假設。 你信任的程式碼可能是錯的。
  • 問五次「為什麼」。 持續直到你找到真正的根本原因。
  • 執行更多故障排除檢查。 更多的 fmt.Println、更多的輸出檢查……

6. 研究整個程式碼庫,而不只是差異

在標記錯誤或提出修復之前,追蹤資料流並檢查上游處理。一個在隔離中看似有問題的函式,在上下文中可能是正確的——呼叫者可能驗證了輸入、中介軟體可能強制了不變量、或周圍的程式碼可能保證了該函式所依賴的條件。

  1. 追蹤呼叫者——誰呼叫了這個函式,以及用什麼值?可以使用程式碼搜尋工具找到呼叫點。
  2. 檢查上游驗證——輸入解析、型別轉換、或鏈中較早的保護子句可能使「錯誤」無法到達。
  3. 閱讀周圍的程式碼——中介軟體、攔截器、或 init 函式可能設定了該函式所依賴的狀態。

當上下文降低了嚴重性但未消除問題時: 仍然以較低優先順序回報,並附上說明解釋哪些上游保證保護了它。加入簡短的內聯註解(例如 // note: safe because caller validates via parseID() which returns uint),以便將推理記錄下來供未來審查者參考。

7. 從簡單開始

有時 fmt.Println 就是本地除錯的正確工具。只有在簡單方法失敗時才升級工具。永遠不要在生產除錯中使用 fmt.Println——請使用 slog

紅旗:你的除錯方式錯了

如果發生以下任何情況,請停止並返回步驟 1:

  • 「先快速修復,之後再調查」——沒有「之後」。找出根本原因。
  • 同時進行多項變更——一次一個假設。
  • 在未了解原因的情況下提出修復——「也許我在這裡加個 nil 檢查……」這是猜測,不是除錯。
  • 每次修復都揭露一個新問題——你在治療症狀。真正的錯誤在別處。
  • 對同一個問題嘗試 3 次以上修復——你的心智模型錯了。重新閱讀程式碼,從頭追蹤資料流。
  • 「在我的機器上可以執行」——你沒有隔離環境差異。
  • 責怪框架/標準函式庫/編譯器——幾乎不可能是 Go 的錯誤。先驗證你的程式碼。

參考檔案

  • 一般除錯方法論——系統化的 10 步驟流程:定義症狀、隔離重現、形成一個假設、測試它、驗證根本原因、並防止回歸。升級指南:何時從 fmt.Println 升級到日誌記錄、pprof、Delve,以及如何避免同時進行多項變更的陷阱。

  • 常見 Go 錯誤——導致 Go 程式碼崩潰的錯誤:nil 指標解參考、介面 nil 陷阱(有型別的 nil ≠ nil)、變數遮蔽、slice/map/defer/error/context 陷阱、競爭條件、JSON 反序列化意外、未關閉資源。每個都附有重現模式與修復方式。

  • 測試驅動除錯——為什麼撰寫一個失敗的測試是除錯的第一步。涵蓋測試隔離技術、表格驅動測試組織以縮小失敗範圍、有用的 go test 旗標(-v-run-count=10 用於不穩定測試)、以及除錯不穩定測試。

  • 並行除錯——競爭條件、死結、goroutine 洩漏。何時使用競爭偵測器(-race)、如何讀取競爭偵測器輸出、隱藏競爭的模式、使用 goleak 偵測洩漏、分析堆疊傾印以尋找死結線索。

  • 效能故障排除——當你的程式碼變慢時:CPU 效能剖析工作流程、記憶體分析(heap vs alloc_objects 效能剖析、尋找洩漏)、鎖競爭(mutex 效能剖析)、以及 I/O 阻塞(goroutine 效能剖析)。如何閱讀火焰圖、識別熱點函式、以及使用基準測試衡量改善。

  • pprof 參考——完整的 pprof 手冊。如何在生產環境中啟用 pprof 端點(含認證)、效能剖析類型(CPU、heap、goroutine、mutex、block、trace)、本地與遠端擷取效能剖析、互動式分析指令(toplistweb)、以及解讀火焰圖。

  • 診斷工具——針對特定症狀的輔助工具。GODEBUG 環境變數(GC 追蹤、排程器追蹤)、Delve 除錯器用於中斷點除錯、逸出分析(go build -gcflags="-m" 以找出非預期的堆積配置)、Go 的執行追蹤器用於了解 goroutine 排程。

  • 生產環境除錯——在不停止服務的情況下除錯線上生產系統。生產環境檢查清單、結構化日誌以便搜尋、安全啟用 pprof(認證、網路隔離)、從執行中的服務擷取效能剖析、網路除錯(tcpdump、netstat)、以及 HTTP 請求/回應檢查。

  • 編譯問題——建置失敗:模組版本衝突、CGO 連結問題、go.mod 與已安裝 Go 版本之間的版本不符、平台特定建置標籤阻止跨平台編譯。

  • 程式碼審查紅旗——程式碼審查時應注意的模式,這些模式可能預示潛在錯誤:未檢查的錯誤、缺少 nil 檢查、並行 map 存取、沒有明確退出機制的 goroutine、迴圈中的 defer 導致資源洩漏。

交叉參考

  • → 請參閱 samber/cc-skills-golang@golang-performance 技能,了解在識別瓶頸後的最佳化模式
  • → 請參閱 samber/cc-skills-golang@golang-observability 技能,了解 Go 執行時期監控的指標、警示與 Grafana 儀表板
  • → 請參閱 samber/cc-skills@promql-cli 技能,了解在生產事件調查期間查詢 Prometheus 指標
  • → 請參閱 samber/cc-skills-golang@golang-concurrencysamber/cc-skills-golang@golang-safetysamber/cc-skills-golang@golang-error-handling 技能