
golang-troubleshooting
熱門系統化地對 Golang 程式進行故障排除 - 找出並修復根本原因。在 Go 程式碼中遇到錯誤、崩潰、死結或非預期行為時使用。涵蓋除錯方法論、常見 Go 陷阱、測試驅動除錯、pprof 設定與擷取、Delve 除錯器、競爭偵測、GODEBUG 追蹤以及生產環境除錯。任何「有問題」的情況從這裡開始。不適用於分析效能剖析或基準測試(→ 請參閱 `samber/cc-skills-golang@golang-benchmark` 技能)或套用最佳化模式(→ 請參閱 `samber/cc-skills-golang@golang-performance` 技能)。
系統化地對 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 程式碼中的錯誤、崩潰、效能問題或非預期行為時:
- 從下方的決策樹開始,識別症狀類別並跳至相關章節。
- 遵循黃金法則——尤其是:修復前先重現、一次一個假設、找出根本原因。
- 逐步執行一般除錯方法論。不要跳過步驟。
- 留意自身推理中的紅旗。如果你發現自己在未了解原因的情況下猜測修復方式,請停止並收集更多證據。
- 逐步升級工具。 從最簡單的診斷開始(
fmt.Println、測試隔離),只有在簡單工具不足時才使用 pprof、Delve 或 GODEBUG。 - 永遠不要提出你無法解釋的修復方案。 如果你不了解錯誤發生的原因,請坦白說出並進一步調查。
快速決策樹
你看到了什麼?
"建置無法編譯"
→ 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. 研究整個程式碼庫,而不只是差異
在標記錯誤或提出修復之前,追蹤資料流並檢查上游處理。一個在隔離中看似有問題的函式,在上下文中可能是正確的——呼叫者可能驗證了輸入、中介軟體可能強制了不變量、或周圍的程式碼可能保證了該函式所依賴的條件。
- 追蹤呼叫者——誰呼叫了這個函式,以及用什麼值?可以使用程式碼搜尋工具找到呼叫點。
- 檢查上游驗證——輸入解析、型別轉換、或鏈中較早的保護子句可能使「錯誤」無法到達。
- 閱讀周圍的程式碼——中介軟體、攔截器、或 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)、本地與遠端擷取效能剖析、互動式分析指令(
top、list、web)、以及解讀火焰圖。 -
診斷工具——針對特定症狀的輔助工具。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-concurrency、samber/cc-skills-golang@golang-safety、samber/cc-skills-golang@golang-error-handling技能





