引言
一般來說軟件系統或者軟件組件都可以簡單的划分為下面三部分:
-
輸入部分,例如:
-
軟件系統外部通信數據/事件輸入;
-
軟件組件API接口參數(同步/異步);
-
-
-
處理部分,例如:
-
軟件系統或者組件內部處理(比如:狀態機處理);
-
-
輸出部分,例如:
-
軟件系統輸出到外部的通信數據;
-
軟件組件調用其他組件API;
-
軟件系統或者組件寫數據到外部(存儲設備,網絡設備,文件,共享內存,數據庫等)
-
為了方便定位問題,上面每一部分都需要覆蓋"不多不少"的日志才行。一般來說,輸入輸出必須加入詳細的日志,方便快速界定問題的所屬,甚至重現問題;處理部分中關鍵動作、主要分支、重要入口、預期不會發生的情況,都必須加入詳細的日志,方便定位問題;
日志是設計的產物
一方面由於日志的受眾有不同的群體,生產工程師,測試工程師,技術支持,開發工程師等,故日志應該具有不同的抽象層次,"ECall is trigger"這句日志打印對生產工程師,測試工程師是友好的,他們完全明白發生了什么事情,"ECallSts = 0"這句日志恐怕只有開發工程師明白發生了什么事情。故我們應該在設計階段就定義好高抽象度,而且目標受眾是非開發工程師的日志,將這些日志作為設計的一個成果物。原因如下:
-
參與上流設計的工程師都是精英工程師,他們有能力定義足夠抽象的日志;
-
設計階段就應該考慮怎么驗證我們的系統,日志作為驗證設計的一種手段,就應該在設計階段被考慮;
-
上流設計工程師通過預定義好最少日志,在項目后期也方便驗證系統實現是否按照預期實現;
另一方面,軟件中出現的錯誤我們可以分為兩類,一類是設計階段就預估到了的,還有一類是由於能力有限設計階段沒有預估到,設計階段就預估到的錯誤我們需要設計相應的日志來覆蓋。
總的來說,在設計階段設計好接口類(輸入&輸出)日志 & 運行類日志(關鍵動作&主要分支&重要入口&預期不會發生的情況等)日志內容,在實現階段不斷完善調試類日志內容。需要強調的是,好的調試類日志,絕不是一遍就可以寫好的,因此團隊要重視日志優化這件事情,不要讓日志的質量持續降低(當項目變大時,項目的代碼也存在一樣的問題,越寫越亂)。
-
在定位問題的過程中完善日志,如果定位問題花費了很長時間,那就說明日志還存在問題,需要進一步完善和優化;
-
需要思考是否可以通過優化日志,來提前預判該問題是否可能發生(如某種資源耗盡而導致的錯誤,可以對資源的使用情況進行記錄)
-
定義好整個團隊記錄日志的規范,保證每個開發記錄的日志格式統一;特別需要說明的是,對於DEBUG級別的日志,也需要定義好清晰的格式,而不是由開發人員自由發揮;
-
整個團隊定期對記錄的日志內容進行Review;
日志怎么設計
日志內容格式
實際項目中日志框架都會包含時間、進程ID、線程ID、級別、模塊名等通用信息,但對日志內容卻沒有嚴格的限制。日志內容應該考慮可讀性與全局唯一性,符合人的理解。
-
日志格式:采用 主語+謂語+賓語+狀語 的格式,日志打印遵從人類的自然語言:
-
主語:會話的發起者
-
謂語:將要具體進行什么樣的操作
-
賓語:行為對象
-
狀語:行為產生的結果
例如: "TBox trigger ECALL successful“
-
-
可讀性:不打無用的、無意義、不完全的日志,例如不是打印 “Unknown message type”,而是打印 “Unknown message type,type= , supported types=[A,B,C]”
-
安全性:避免在日志中輸出一些敏感信息,例如密碼與Key等;
-
全局唯一性:日志內容應該全局唯一,方便ctrl+f全局查找。
-
信息充足性:在實踐中經常發生日志不夠的情況,在輸出日志時,要注意一並輸出上下文相關信息,比如函數的入參,函數返回值,內部主要數據等。同時也要注意日志內容不宜太長,最好顯示在一行內(最多50個字符)
日志等級划分
一般來說,日志等級分為五種級別,從高到低分別是:FATAL,ERROR、WARN、INFO、DEBUG:
-
FATAL:系統已經無法正常工作,屬於最嚴重的日志級別,一般來說應該非常少見。比如
-
引發整個系統失敗的錯誤發生了;
-
系統無法正常啟動;
-
某個進程遇到無法恢復的錯誤而自動退出;
-
關鍵組件啟動失敗;
-
-
ERROR:系統部分功能已經無法正常使用,緊急程度要低於FATAL級別。比如:
-
調用外部系統接口返回失敗,重試多次后依然失敗;
-
系統資源使用(RAM,CPU)超過預期;
-
-
WARN:不會影響程序繼續執行后續的邏輯,更多是一種重要提示需要引起重視。程序可以容忍這些信息,不過它們應該被及時地檢查及修復。比如:
-
組件輸入輸出異常時需要打印(API參數值異常/同步API返回值異常/異步事件超時);
-
備份數據恢復出錯,采用默認備份數據;
-
-
INFO:記錄系統的正常運行狀態,通過查看INFO級別的日志,方便很快地對系統中出現的WARN,ERROR,FATAL錯誤進行定位。比如:
-
組件相關信息打印(組件初始化完成了/正常輸入輸出/內部狀態機發生遷移/主要分支入口/主要操作成功了/異步請求執行結果輸出)
-
系統操作行為:如開啟/銷毀線程,打開/關閉連接,定時任務觸發等;
-
資源或狀態變化:系統初始化成功,關鍵資源的統計信息;
-
非預期執行:為程序在“有可能”執行到的地方打印日志,如switch case語句塊中的default;
-
-
DEBUG:該級別日志的主要作用是對系統每一步的運行狀態進行精確的記錄。通過該種日志,可以查看某一個操作每一步的執行過程,可以准確定位是何種操作,何種參數,何種順序導致了某種錯誤的發生。可以保證在不重現錯誤的情況下,也可以通過DEBUG(或TRACE)級別的日志對問題進行診斷。比如:
-
方便CT & IT測試添加的日志
-
非API函數的入參打印
-
快速定位
如前面所說,系統或者組件一般可以分為三部分,輸入->處理->輸出。故在調查具體的bug時,我們需要能知道以下信息:
-
本軟件系統/本軟件組件輸入輸出是否正常;
-
如果輸出不正常,到底是輸入異常導致 OR 軟件系統或者組件處理出錯導致;
-
如果是系統或者組件內部出錯,要能通過日志定位到具體原因。
-
鑒於bug很多都是時序混亂、數據非法等造成的,故需要通過日志快速知道問題發生時各組件交互時序,系統/組件輸入數據是否非法等。
在以往的項目中經常遇到這個問題,幾乎總有人會在問題發生時說,這個日志基本上沒有什么用處/或者這個日志我不知道想要干什么,但是每個人似乎又不能說出具體日志應該怎么輸出才對。 故能否像MISRC規范對代碼提出一些硬性要求一樣,對日志也制定一些最低要求。
===================================================================================================================================
- FSR1 [強制]:項目團隊要制定日志輸出規范.
- FSR2 [強制]:項目團隊要制定日志評審流程。
- FSR3 [強制]:設計階段設計好日志(邊界類&運行類日志)。
- FSR4 [建議]:設計階段設計用於系統測試&結合測試的日志。
- FSR5 [強制]:系統的輸入輸出,必須覆蓋詳細的日志,日志中包含輸入或者輸出的一些關鍵參數。
- FSR6 [強制]:組件的輸入輸出,必須覆蓋詳細的日志,日志中包含輸入或者輸出的一些關鍵參數。
- FSR7 [強制]:組件必須至少包含以下4種關鍵詞的日志,用來診斷組件是否健康。
- FSR7.1 [強制]:程序流關鍵分支上打印用一種關鍵詞,通過篩選該關鍵詞能快速定位問題出現在那個時序段。
- FSR7.2 [強制]:設計時考慮不可能發生的分支上打印用一種關鍵詞,通過篩選該關鍵詞能快速是否進入了以為不可能進入的分支。
- FSR7.3 [強制]:函數入參范圍檢查不合格打印用一種關鍵詞,通過篩選該關鍵詞能快速知道是否函數入參超過范圍。
- FSR7.4 [強制]:組件輸入輸出打印用一種關鍵詞,通過篩選該關鍵詞能快速知道組件是否正常。
- FSR7.5 [強制]:上述4種關鍵詞在所有組件都是相同的。
- FSR8 [建議]: 日志內容總體上來說要盡可能少(不意味着信息缺失),方便快速分析。
- FSR9 [強制]: 日志內容要包含時間戳,日志等級,模塊名等關鍵詞方便快速篩選。
- FSR10 [建議]: 錯誤之處覆蓋的日志要包含上下文相關信息(函數的入參/函數返回值/內部主要數據值等)。
- FSR11[強制]: 不循環打印相同內容日志。
- FSR12[強制]: 每行日志內容不宜過長,不超過50字。
- FSR13[建議]: 日志內容要遵守上述日志內容輸出格式。
- FSR14[強制]: 日志登記要遵守上述日志等級划分要求 。
- FSR15[強制]: 系統版本號/各部件版本號/數據庫版本號等相關版本信息需要以最高等級輸出。