60秒定位問題,十倍程式設計師的Debug日常
作者:陶建輝
這是我在 2020 年 5 月寫的一篇內部部落格,當時是希望研發和技術支援同學能夠幫助使用者快速定位 Bug,解決問題。2020 年 12 月我又迭代了一版,並還針對此進行了內部的培訓。這段時間我也一直在關注微信群中的提問,發現有不少使用者覺得分析 TDengine 的日誌或任何其他分散式系統的日誌是有難度的。現在將我這篇部落格公開,以分析 TDengine 的日誌為例,介紹一套方法,如果你能掌握,那麼分析分散式系統的日誌就會變得極其之簡單。
TDengine 是一個集群系統,任何一個操作,都會涉及到 APP、taosc、mnode 和 vnode 等邏輯節點。這些節點之間是通過 Socket 進行通訊的。而且在測試中,可能有多個 TDengine 例項,這讓分析變得更為複雜。對於一個操作,如何將不同邏輯節點的日誌匹配串通起來,高效過濾處理,成為分析問題的關鍵。
本文總結了一套方法,能夠讓大家快速找到 Bug 所在。
開啟相關日誌開關
TDengine 每個獨立的模組都有自己的 debugFlag, 包括 taosc, dnode, vnode, mnode, tsdb, wal, sync, query, rpc, timer 等。目前每個模組的日誌輸出可以控制到:
- Fatal/Error,錯誤,日誌上會顯示 ERROR
- Warning,警告, 日誌上會顯示 WARN
- Info,重要資訊
- Debug,一般資訊
- Trace,非常詳細且反覆出現的除錯資訊
- Dump,原始資料
輸出的日誌可以控制輸出到:
- 檔案
- 螢幕
上述的控制全部由 debugFlag 的一個位元組控制,這個位元組裡的 bit 圖如下:
因此,如果要將 error, warning, info,debug 輸出到日誌檔案,那麼 debug 要設定為:135;如果還想輸出 trace 級別的日誌,那麼 debug 要設定為:143;如果僅僅輸出 error 和 warning, debug 設定為 131。正常情況下,建議將 debug 設定為 135 即可。
每個模組的 debug flag 的設定全部由配置檔案 taos.cfg 控制,具體各模組的引數以及日誌顯示的模組名如下:
如果嫌配置引數過多,最簡單的方式,是設定 debug 的總引數 debugFlag, 這個引數設定後,除 tmr 日誌外,所有模組的 debug 全部統一設定為同一個引數 debugFlag。debugFlag 的預設值為 0,當 debugFlag 為非 0 值時,將會覆蓋所有的日誌配置引數。
除非特殊 case, 不建議設定 util, timer 的 debugFlag 為 135,設定為 131 合適。
日誌檔案
TDengine 會生成客戶端以及伺服器端日誌,存放在日誌目錄裡,預設日誌目錄是/var/log/taos, 但可以通過修改 taos.cfg 裡的配置引數 logDir 指定
- 客戶端日誌檔名為 taoslogY.X (因為可以執行多個客戶端,因此一臺機器上可以生成多個日誌檔案)
- 伺服器端日誌檔案為 taosdlog.X
日誌檔案的大小有控制,達到一定行數(taos.cfg 裡配置引數 numOfLogLines 控制)後,會生成新的日誌檔案。但 TDengine 僅僅保留兩個日誌檔案,檔名以 0 或 1 結尾,交替。
日誌格式:
日誌檔案,從左到右,分為四大塊
- 時間戳,精確到微妙
- 執行緒 ID,因為是多執行緒,這個引數很重要,因為只有同一個執行緒輸出的日誌才是有時序保證的,是按設計的 flow 輸出的
- 模組名,三個字母
- 每個模組輸出的日誌
分析日誌的幾大步驟
當測試或客戶報告一個 Bug,無論是手動還是自動,都是執行某個具體操作發生的。這個具體操作一般都是執行一個 SQL 語句。這個問題可能是客戶端導致,也可能是伺服器程式碼導致。下面以 create table 為例,解釋日誌的分析,為便於聚焦解釋,圖中去掉時間戳。
先看客戶端
最先需要檢視的是客戶端日誌,示例截圖如下:
- 先找到出問題的 SQL 語句,在客戶端日誌裡搜尋 “SQL: “,就可以看到(截圖第二行)。日誌裡搜尋“SQL result:”(截圖第 11 行), 如果成功,會顯示 ”SQL result:success”, 否則會顯示“SQL result: xxxx”, 其中 xxxx 是錯誤資訊。如何快速找到失敗的 SQL,需要知道大概的時間範圍,SQL 語句是什麼,這麼搜尋會很快。
- taoc 的日誌資料,有個很重要的引數是 pSql,是分配給內部 SQL Obj 的一個地址。taosc 把這個地址資訊放在所有 taosc 日誌的最前面,緊接 TSC 之後。這個值很關鍵,是傳統客戶端與伺服器日誌的關鍵。在上面的截圖中,用綠色背景標出。
- pSql 這個引數會作為 ahandle 傳遞至 RPC 模組,RPC 在所有訊息裡,都會將其列印處理(綠色背景)。因為很多模組都會呼叫 RPC 模組,RPC 也會把誰呼叫的打印出來,比如截圖中,是 TSC 呼叫的,就會列印 RPC TSC 出來。
- RPC 會把訊息 create-table 發往伺服器,RPC 日誌會打印出來(截圖第 8 行), 告知發往到了哪個 dnode 的 End Point, 截圖中顯示發往了 hostname: 9be7010a917e, port 為 6030。如果有問題,那我們就需要檢查這個 End Point 所在的伺服器日誌了。
- 能看到,RPC 模組收到了伺服器的迴應,但為避免轉換消耗資源,日誌僅僅顯示了 16 進位制的 IP 地址(截圖第 9 行,0x20012ac)以及埠號。RPC 模組的日誌很關鍵,因為它把各個邏輯節點串通起來。
再看伺服器
分析了客戶端日誌,伺服器日誌很關鍵,下面仍然以 create-table 為例,請看截圖:
- 從客戶端日誌裡,找到 pSql,值是 0x5572c4fab3a0,因此在 taosdlog 裡直接搜尋 0x5572c4fab3a0,就可以看到截圖中綠色背景的日誌。因此 pSql 是將客戶端與伺服器日誌串起來很重要的引數。
- 對於 create-table 這個具體操作而言,是有 mnode 處理。截圖中,是因為建立第一張表,因此需要先建立 vnode, 然後建立 table 等系列操作,牽涉到很多模組,就不細解釋。
- 最後,mnode 建立完 table, 通過 RPC 模組發回 response(截圖第 52 行,最後一行),因此可以清晰的看出伺服器工作正常。
注意:dnode 模組收到訊息後,會根據訊息型別,將訊息分發到 mnode, vnode 的訊息佇列。然後工作執行緒會消費訊息佇列裡的訊息,對訊息進行處理。對於 vnode 而言,裡面的子模組 tsdb, wal, sync, cq 都是單執行緒執行的,因此在找到 pSql 之後(截圖第二行),需要根據執行緒的 ID,順序往下看,就能知道整個流程,很好分析。
幾個關鍵點
- 先找到失敗 SQL 語句
- 找到 pSql 的值,拷貝下來, 假設為 xxxxx
- grep “xxxxx” taoslogx.x, 找到與這條 SQL 相關的客戶端日誌,看能否找到問題
- 開啟 taosdlog 伺服器日誌,搜尋 pSql 的值 xxxxx, 檢查時間戳,看是否是失敗的那條操作
- 然後分析伺服器日誌
RPC 模組的訊息十分關鍵。有一點很重要,對於每一條 RPC 訊息,都會列印 parse code: xx, 這是協議解析結果,0 表示沒有問題,其他值說明協議解析就不成功。但同時,訊息本身還有 code: 0xXX, 這是傳送方帶來的錯誤碼,一般是伺服器發往客戶端的,正確的話,code 是 0,否則就是報錯。
另外一個日誌匹配方法
客戶端通過 RPC 模組發出一條訊息時,日誌上帶有類似
sig:0x01000000:0x01000000:55893
這是 RPC 的 source ID, dest ID, 以及 transcation ID,三個引數組合在一起,能唯一標識來自一個客戶端的連結。每條新訊息的傳送,都會將 transcation ID 加一,因此在一段時間內具有唯一性(transcation ID 是兩位元組,會迴圈的)。
1.6 版本只能依賴 sig 這個字串將客戶端與伺服器日誌匹配起來,但需要看很多上下文,因此麻煩,效率不夠。
2.0 版將 pSql 傳至伺服器側,這樣客戶端與伺服器的日誌匹配將大大加速。
熟悉日誌的方法
- 首先要了解 TDengine 的設計,瞭解每個主要操作的流程。
- 開啟所有日誌開關(將 debugFlag 設為 135), 所有的 SQL 語句都執行一次,對照每條 SQL,檢查對應的客戶端與伺服器日誌。
檢視客戶端執行的 SQL 語句
客戶端會產生很多日誌,檢視執行的 SQL 語句,便於分析和重複問題。找出系統到底執行了什麼樣的 SQL 語句,有幾種方法
- 如果客戶端日誌開啟,執行: grep “SQL: ” taoslog*, 會看到日誌中所有執行的 SQL 語句。
- 如果用 taos,手工執行 SQL 語句,請在 home 目錄裡,檢視隱藏的檔案.taos_history, 裡面會有 taos 執行的所有歷史命令。
- 對客戶端進行配置,在配置檔案中,將 tscEnableRecordSql 引數設定為 1,即將客戶端輸入的 SQL 語句列印到單獨的檔案(tscnote-xxxx.0,xxxx 是 pid),與客戶端日誌所在目錄相同。
- 對於 resetful 介面,在 taosd 的配置檔案中將 httpEnableRecordSql 引數設定為 1, 會將 htpp 請求列印到單獨的檔案(httpnote.0),與服務端日誌所在目錄相同。
動態修改日誌
有時不能重啟伺服器或客戶端,但日誌的設定又不正確,這個時候需要動態設定,執行如下幾步:
show dnodes;// 查詢每個 dnode 的 ID alter dnode id debugFlag 143; // 設定相應的 debugFlag
其中第二步的 id 是第一步獲取的。
有時需要把後續日誌輸出到一個新檔案,方便日誌的檢視及搜尋,執行:
alter dnode id resetlog;
有時 shell 根本連結不上,這時可以在 taosd 執行的機器,給程序傳送 SIGUSR1 命令,如:
kill -SIGUSR1 pidxxx
原文首發於:https://mp.weixin.qq.com/s/LUz1niYajOR35UpOlf