熟读《阿里巴巴java开发手册》(二、异常日志)
目錄
(一) 異常處理
(二) 日志規約
(一) 異常處理
1. 【強制】 Java 類庫中定義的可以通過預檢查方式規避的 RuntimeException 異常不應該通過 catch 的方式來處理,比如: NullPointerException, IndexOutOfBoundsException 等等。
說明: 無法通過預檢查的異常除外,比如,在解析字符串形式的數字時, 可能存在數字格式錯誤, 不得不
通過 catch NumberFormatException 來實現。
正例: if (obj != null) {...}
反例: try { obj.method(); } catch (NullPointerException e) {…}
2. 【強制】 異常不要用來做流程控制,條件控制。
說明: 異常設計的初衷是解決程序運行中的各種意外情況,且異常的處理效率比條件判斷方式要低很多。
3. 【強制】 catch 時請分清穩定代碼和非穩定代碼,穩定代碼指的是無論如何不會出錯的代碼。對于非穩定代碼的 catch 盡可能進行區分異常類型,再做對應的異常處理。
說明: 對大段代碼進行 try-catch,使程序無法根據不同的異常做出正確的應激反應,也不利于定位問題,這是一種不負責任的表現。
正例: 用戶注冊的場景中,如果用戶輸入非法字符, 或用戶名稱已存在, 或用戶輸入密碼過于簡單,在程序上作出分門別類的判斷,并提示給用戶。
4. 【強制】 捕獲異常是為了處理它,不要捕獲了卻什么都不處理而拋棄之,如果不想處理它,請將該異常拋給它的調用者。最外層的業務使用者,必須處理異常,將其轉化為用戶可以理解的內容。
5. 【強制】 有 try 塊放到了事務代碼中, catch 異常后,如果需要回滾事務,一定要注意手動回滾事務。
6. 【強制】 finally 塊必須對資源對象、流對象進行關閉,有異常也要做 try-catch。
說明: 如果 JDK7 及以上,可以使用 try-with-resources 方式。
7. 【強制】 不要在 finally 塊中使用 return。
說明: try 塊中的 return 語句執行成功后,并不馬上返回,而是繼續執行 finally 塊中的語句,如果此處存在 return 語句,則在此直接返回,無情丟棄掉 try 塊中的返回點。
反例:
8. 【強制】 捕獲異常與拋異常,必須是完全匹配,或者捕獲異常是拋異常的父類。
說明: 如果預期對方拋的是繡球,實際接到的是鉛球,就會產生意外情況。
9. 【強制】 在調用 RPC、二方包、或動態生成類的相關方法時,捕捉異常必須使用 Throwable類來進行攔截。
說明: 通過反射機制來調用方法,如果找不到方法,拋出 NoSuchMethodException。什么情況會拋出NoSuchMethodError 呢?二方包在類沖突時,仲裁機制可能導致引入非預期的版本使類的方法簽名不匹配,或者在字節碼修改框架(比如: ASM)動態創建或修改類時,修改了相應的方法簽名。這些情況,即使代碼編譯期是正確的,但在代碼運行期時,會拋出 NoSuchMethodError。
10.【推薦】 方法的返回值可以為 null,不強制返回空集合,或者空對象等,必須添加注釋充分說明什么情況下會返回 null 值。
說明: 本手冊明確防止 NPE 是調用者的責任。即使被調用方法返回空集合或者空對象,對調用者來說,也并非高枕無憂,必須考慮到遠程調用失敗、 序列化失敗、運行時異常等場景返回 null 的情況。
11.【推薦】 防止 NPE,是程序員的基本修養,注意 NPE 產生的場景:
1) 返回類型為基本數據類型, return 包裝數據類型的對象時,自動拆箱有可能產生 NPE。
反例: public int f() { return Integer 對象}, 如果為 null,自動解箱拋 NPE。
2) 數據庫的查詢結果可能為 null。
3) 集合里的元素即使 isNotEmpty,取出的數據元素也可能為 null。
4) 遠程調用返回對象時,一律要求進行空指針判斷,防止 NPE。
5) 對于 Session 中獲取的數據,建議進行 NPE 檢查,避免空指針。
6) 級聯調用 obj.getA().getB().getC();一連串調用,易產生 NPE。
正例: 使用 JDK8 的 Optional 類來防止 NPE 問題。
12.【推薦】 定義時區分 unchecked / checked 異常,避免直接拋出 new RuntimeException(),更不允許拋出 Exception 或者 Throwable,應使用有業務含義的自定義異常。推薦業界已定義過的自定義異常,如: DAOException / ServiceException 等。
13.【參考】 對于公司外的 http/api 開放接口必須使用“錯誤碼” ;而應用內部推薦異常拋出;跨應用間 RPC 調用優先考慮使用 Result 方式,封裝 isSuccess()方法、 “錯誤碼” 、 “錯誤簡短信息” 。
說明: 關于 RPC 方法返回方式使用 Result 方式的理由:
1)使用拋異常返回方式,調用方如果沒有捕獲到就會產生運行時錯誤。
2)如果不加棧信息,只是 new 自定義異常,加入自己的理解的 error message,對于調用端解決問題的幫助不會太多。如果加了棧信息,在頻繁調用出錯的情況下,數據序列化和傳輸的性能損耗也是問題。
14.【參考】 避免出現重復的代碼( Don't Repeat Yourself) ,即 DRY 原則。
說明: 隨意復制和粘貼代碼,必然會導致代碼的重復,在以后需要修改時,需要修改所有的副本,容易遺漏。必要時抽取共性方法,或者抽象公共類,甚至是組件化。
正例: 一個類中有多個 public 方法,都需要進行數行相同的參數校驗操作,這個時候請抽取:
private boolean checkParam(DTO dto) {...}
(二) 日志規約
1. 【強制】 應用中不可直接使用日志系統( Log4j、 Logback) 中的 API,而應依賴使用日志框架SLF4J 中的 API,使用門面模式的日志框架,有利于維護和各個類的日志處理方式統一。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Test.class);
2. 【強制】 所有日志文件至少保存 15 天,因為有些異常具備以“周” 為頻次發生的特點。 網絡運行狀態、安全相關信息、系統監測、管理后臺操作、用戶敏感操作需要留存相關的網絡日志不少于 6 個月。
3. 【強制】 應用中的擴展日志( 如打點、臨時監控、訪問日志等) 命名方式:appName_logType_logName.log。 logType:日志類型, 如 stats/monitor/access 等; logName:日志描述。這種命名的好處:通過文件名就可知道日志文件屬于什么應用,什么類型,什么目的,也有利于歸類查找。
說明: 推薦對日志進行分類, 如將錯誤日志和業務日志分開存放,便于開發人員查看,也便于通過日志對系統進行及時監控。
正例: force-web 應用中單獨監控時區轉換異常,如: force_web_timeZoneConvert.log
4. 【強制】 在日志輸出時,字符串變量之間的拼接使用占位符的方式。
說明: 因為 String 字符串的拼接會使用 StringBuilder 的 append()方式,有一定的性能損耗。使用占位符僅是替換動作,可以有效提升性能。
正例: logger.debug("Processing trade with id: {} and symbol: {}", id, symbol);
5. 【強制】 對于 trace/debug/info 級別的日志輸出,必須進行日志級別的開關判斷。
說明: 雖然在 debug(參數)的方法體內第一行代碼 isDisabled(Level.DEBUG_INT)為真時( Slf4j 的常見實現 Log4j 和 Logback),就直接 return,但是參數可能會進行字符串拼接運算。此外,如果debug(getName())這種參數內有 getName()方法調用,無謂浪費方法調用的開銷。
正例:
6. 【強制】 避免重復打印日志,浪費磁盤空間,務必在 log4j.xml 中設置 additivity=false。
正例: <logger name="com.taobao.dubbo.config" additivity="false">
7. 【強制】 異常信息應該包括兩類信息:案發現場信息和異常堆棧信息。如果不處理,那么通過關鍵字 throws 往上拋出。
正例: logger.error(各類參數或者對象 toString() + "_" + e.getMessage(), e);
8. 【推薦】 謹慎地記錄日志。生產環境禁止輸出 debug 日志; 有選擇地輸出 info 日志; 如果使用 warn 來記錄剛上線時的業務行為信息,一定要注意日志輸出量的問題,避免把服務器磁盤撐爆,并記得及時刪除這些觀察日志。
說明: 大量地輸出無效日志,不利于系統性能提升,也不利于快速定位錯誤點。 記錄日志時請思考:這些日志真的有人看嗎?看到這條日志你能做什么?能不能給問題排查帶來好處?
9. 【推薦】 可以使用 warn 日志級別來記錄用戶輸入參數錯誤的情況,避免用戶投訴時,無所適從。如非必要,請不要在此場景打出 error 級別,避免頻繁報警。
說明: 注意日志輸出的級別, error 級別只記錄系統邏輯出錯、異常或者重要的錯誤信息。
10.【推薦】 盡量用英文來描述日志錯誤信息,如果日志中的錯誤信息用英文描述不清楚的話使用中文描述即可,否則容易產生歧義。 【強制】 國際化團隊或海外部署的服務器由于字符集問題,使用全英文來注釋和描述日志錯誤信息。
?
總結
以上是生活随笔為你收集整理的熟读《阿里巴巴java开发手册》(二、异常日志)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 熟读《阿里巴巴java开发手册》(一、编
- 下一篇: 熟读《阿里巴巴java开发手册》(三、单