二、異常日志
(一)、錯誤碼
- 錯誤碼的制定原則:快速溯源、溝通標準化
- 錯誤碼不體現版本號和錯誤等級信息
- 全部正常,但不得不填充錯誤碼時返回五個零:00000
- 錯誤碼為字符串類型,共 5 位,分成兩個部分:錯誤產生來源+四位數字編號
- 編號不與公司業務架構,更不與組織架構掛鉤,以先到先得的原則在統一平臺上進行,審批生效,編號即被永久固定
- 錯誤碼使用者避免隨意定義新的錯誤碼
- 錯誤碼不能直接輸出給用戶作為提示信息使用
- 錯誤碼之外的業務信息由 error_message 來承載,而不是讓錯誤碼本身涵蓋過多具體業務屬性
- 在獲取第三方服務錯誤碼時,向上拋出允許本系統轉義,由 C 轉為 B,并且在錯誤信息上帶上原有的第三方錯誤碼
- 錯誤碼分為一級宏觀錯誤碼、二級宏觀錯誤碼、三級宏觀錯誤碼
- 錯誤碼的后三位編號與 HTTP 狀態碼沒有任何關系
- 錯誤碼有利于不同文化背景的開發者進行交流與代碼協作
- 錯誤碼即人性,感性認知+口口相傳,使用純數字來進行錯誤碼編排不利于感性記憶和分類
(二)、異常處理 - Java 類庫中定義的可以通過預檢查方式規避的 RuntimeException 異常不應該通過 catch 的方式來處理,比如:NullPointerException,IndexOutOfBoundsException 等等
- 異常捕獲后不要用來做流程控制,條件控制
- catch 時請分清穩定代碼和非穩定代碼,穩定代碼指的是無論如何不會出錯的代碼。對于非穩定代碼的 catch 盡可能進行區分異常類型,再做對應的異常處理
- 捕獲異常是為了處理它,不要捕獲了卻什么都不處理而拋棄之,如果不想處理它,請將該異常拋給它的調用者。最外層的業務使用者,必須處理異常,將其轉化為用戶可以理解的內容
- 事務場景中,拋出異常被 catch 后,如果需要回滾,一定要注意手動回滾事務
- finally 塊必須對資源對象、流對象進行關閉,有異常也要做 try-catch
- 不要在 finally 塊中使用 return
- 捕獲異常與拋異常,必須是完全匹配,或者捕獲異常是拋異常的父類
- 在調用 RPC、二方包、或動態生成類的相關方法時,捕捉異常使用 Throwable 類進行攔截
- 方法的返回值可以為 null,不強制返回空集合,或者空對象等,必須添加注釋充分說明什么情況下會返回 null 值
- 注意 NPE 產生的場景:
- 返回類型為基本數據類型,return 包裝數據類型的對象時,自動拆箱有可能產生 NPE
- 數據庫的查詢結果可能為 null
- 集合里的元素即使 isNotEmpty,取出的數據元素也可能為 null
- 遠程調用返回對象時,一律要求進行空指針判斷,防止 NPE
- 對于 Session 中獲取的數據,建議進行 NPE 檢查,避免空指針
- 級聯調用 obj.getA().getB().getC();一連串調用,易產生 NPE
- 定義時區分 unchecked / checked 異常,避免直接拋出 new RuntimeException(),更不允許拋出 Exception 或者 Throwable,應使用有業務含義的自定義異常。推薦業界已定義過的自定義異常,如:DAOException / ServiceException 等
- 對于公司外的 http / api 開放接口必須使用錯誤碼,而應用內部推薦異常拋出;跨應用間RPC 調用優先考慮使用 Result 方式,封裝 isSuccess() 方法、錯誤碼、錯誤簡短信息;應用內部推薦異常拋出
(三)、日志規約 - 應用中不可直接使用日志系統(Log4j、Logback)中的 API,而應依賴使用日志框架(SLF4J、JCL—Jakarta Commons Logging)中的 API,使用門面模式的日志框架,有利于維護和各個類的日志處理方式統一
- 日志文件至少保存 15 天,因為有些異常具備以“周”為頻次發生的特點。對于當天日志,以“應用名.log”來保存,保存在/{統一目錄}/{應用名}/logs/目錄下,過往日志格式為:{logname}.log.{保存日期},日期格式:yyyy-MM-dd
- 根據國家法律,網絡運行狀態、網絡安全事件、個人敏感信息操作等相關記錄,留存的日志不少于六個月,并且進行網絡多機備份
- 應用中的擴展日志(如打點、臨時監控、訪問日志等)命名方式:appName_logType_logName.log。logType:日志類型,如 stats / monitor / access 等;logName:日志描述。這種命名的好處:通過文件名就可知道日志文件屬于什么應用,什么類型,什么目的,也有利于歸類查找
- 在日志輸出時,字符串變量之間的拼接使用占位符的方式
- 對于 trace / debug / info 級別的日志輸出,必須進行日志級別的開關判斷
- 避免重復打印日志,浪費磁盤空間,務必在日志配置文件中設置 additivity=false
- 生產環境禁止使用 System.out 或 System.err 輸出或使用 e.printStackTrace() 打印異常堆棧
- 異常信息應該包括兩類信息:案發現場信息和異常堆棧信息。如果不處理,那么通過關鍵字throws 往上拋出
- 日志打印時禁止直接用 JSON 工具將對象轉換成 String
- 謹慎地記錄日志。生產環境禁止輸出 debug 日志;有選擇地輸出 info 日志;如果使用 warn來記錄剛上線時的業務行為信息,一定要注意日志輸出量的問題,避免把服務器磁盤撐爆,并記得及時刪除這些觀察日志
- 可以使用 warn 日志級別來記錄用戶輸入參數錯誤的情況,避免用戶投訴時,無所適從。如非必要,請不要在此場景打出 error 級別,避免頻繁報警
- 盡量用英文來描述日志錯誤信息,如果日志中的錯誤信息用英文描述不清楚的話使用中文描述即可,否則容易產生歧義
- 為了保護用戶隱私,日志文件中的用戶敏感信息需要進行脫敏處理