閱讀631 返回首頁    go 阿裏雲 go 技術社區[雲棲]


阿裏巴巴 Java 開發手冊之編程規約(二)-------我的經驗(逐步完善中)

二、異常日誌

(一) 異常處理

1. 【強製】Java 類庫中定義的一類RuntimeException可以通過預先檢查進行規避,而不應該通過catch 來處理,比如:IndexOutOfBoundsException,NullPointerException等等。

說明:無法通過預檢查的異常除外,如在解析一個外部傳來的字符串形式數字時,通過catch NumberFormatException來實現。 正例:if (obj != null) {...} 反例:try { obj.method() } catch (NullPointerException e) {...}

  1. 【強製】異常不要用來做流程控製,條件控製,因為異常的處理效率比條件分支低。
  2. 【強製】對大段代碼進行try-catch,這是不負責任的表現。catch時請分清穩定代碼和非穩定代碼,穩定代碼指的是無論如何不會出錯的代碼。對於非穩定代碼的catch盡可能進行區分異常類型,再做對應的異常處理。
  3. 【強製】捕獲異常是為了處理它,不要捕獲了卻什麼都不處理而拋棄之,如果不想處理它,請將該異常拋給它的調用者。最外層的業務使用者,必須處理異常,將其轉化為用戶可以理解的內容。
  4. 【強製】有try塊放到了事務代碼中,catch異常後,如果需要回滾事務,一定要注意手動回滾事務。
  5. 【強製】finally塊必須對資源對象、流對象進行關閉,有異常也要做try-catch。 說明:如果JDK7及以上,可以使用try-with-resources方式。
  6. 【強製】不能在finally塊中使用return,finally塊中的return返回後方法結束執行,不會再執行try塊中的return語句。
  7. 【強製】捕獲異常與拋異常,必須是完全匹配,或者捕獲異常是拋異常的父類。 說明:如果預期對方拋的是繡球,實際接到的是鉛球,就會產生意外情況。
  8. 【推薦】方法的返回值可以為null,不強製返回空集合,或者空對象等,必須添加注釋充分說明什麼情況下會返回null值。調用方需要進行null判斷防止NPE問題。 說明:本規約明確防止NPE是調用者的責任。即使被調用方法返回空集合或者空對象,對調用者來說,也並非高枕無憂,必須考慮到遠程調用失敗,運行時異常等場景返回null的情況。 阿裏巴巴 Java 開發手冊 ——禁止用於商業用途,違者必究—— 21 / 37
  9. 【推薦】防止NPE,是程序員的基本修養,注意NPE產生的場景: 1) 返回類型為包裝數據類型,有可能是null,返回int值時注意判空。 反例: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問題。
  10. 【推薦】在代碼中使用“拋異常”還是“返回錯誤碼”,對於公司外的http/api開放接口必須使用“錯誤碼”;而應用內部推薦異常拋出;跨應用間RPC調用優先考慮使用Result方式,封裝isSuccess、“錯誤碼”、“錯誤簡短信息”。 說明:關於RPC方法返回方式使用Result方式的理由: 1)使用拋異常返回方式,調用方如果沒有捕獲到就會產生運行時錯誤。 2)如果不加棧信息,隻是new自定義異常,加入自己的理解的error message,對於調用端解決問題的幫助不會太多。如果加了棧信息,在頻繁調用出錯的情況下,數據序列化和傳輸的性能損耗也是問題。
  11. 【推薦】定義時區分unchecked / checked 異常,避免直接使用RuntimeException拋出,更不允許拋出Exception或者Throwable,應使用有業務含義的自定義異常。推薦業界已定義過的自定義異常,如:DAOException / ServiceException等。
  12. 【參考】避免出現重複的代碼(Don’t Repeat Yourself),即DRY原則。 說明:隨意複製和粘貼代碼,必然會導致代碼的重複,在以後需要修改時,需要修改所有的副本,容易遺漏。必要時抽取共性方法,或者抽象公共類,甚至是共用模塊。 正例:一個類中有多個public方法,都需要進行數行相同的參數校驗操作,這個時候請抽取: private boolean checkParam(DTO dto) {...} (二) 日誌規約
  13. 【強製】應用中不可直接使用日誌係統(Log4j、Logback)中的API,而應依賴使用日誌框架SLF4J中的API,使用門麵模式的日誌框架,有利於維護和各個類的日誌處理方式統一。 import org.slf4j.Logger; import org.slf4j.LoggerFactory; private static final Logger logger = LoggerFactory.getLogger(Abc.class);
  14. 【強製】日誌文件推薦至少保存15天,因為有些異常具備以“周”為頻次發生的特點。
  15. 【強製】應用中的擴展日誌(如打點、臨時監控、訪問日誌等)命名方式:appName_logType_logName.log。logType:日誌類型,推薦分類有 阿裏巴巴 Java 開發手冊 ——禁止用於商業用途,違者必究—— 22 / 37 stats/desc/monitor/visit等;logName:日誌描述。這種命名的好處:通過文件名就可知道日誌文件屬於什麼應用,什麼類型,什麼目的,也有利於歸類查找。 正例:mppserver應用中單獨監控時區轉換異常,如: mppserver_monitor_timeZoneConvert.log 說明:推薦對日誌進行分類,錯誤日誌和業務日誌盡量分開存放,便於開發人員查看,也便於通過日誌對係統進行及時監控。
  16. 【強製】對trace/debug/info級別的日誌輸出,必須使用條件輸出形式或者使用占位符的方式。 說明:logger.debug("Processing trade with id: " + id + " symbol: " + symbol); 如果日誌級別是warn,上述日誌不會打印,但是會執行字符串拚接操作,如果symbol是對象,會執行toString()方法,浪費了係統資源,執行了上述操作,最終日誌卻沒有打印。 正例:(條件) if (logger.isDebugEnabled()) { logger.debug("Processing trade with id: " + id + " symbol: " + symbol); } 正例:(占位符) logger.debug("Processing trade with id: {} symbol : {} ", id, symbol);
  17. 【強製】避免重複打印日誌,浪費磁盤空間,務必在log4j.xml中設置additivity=false。 正例:
  18. 【強製】異常信息應該包括兩類信息:案發現場信息和異常堆棧信息。如果不處理,那麼通過關鍵字throws往上拋出。 正例:logger.error(各類參數或者對象toString + "_" + e.getMessage(), e);
  19. 【推薦】謹慎地記錄日誌。生產環境禁止輸出debug日誌;有選擇地輸出info日誌;如果使用warn來記錄剛上線時的業務行為信息,一定要注意日誌輸出量的問題,避免把服務器磁盤撐爆,並記得及時刪除這些觀察日誌。 說明:大量地輸出無效日誌,不利於係統性能提升,也不利於快速定位錯誤點。記錄日誌時請思考:這些日誌真的有人看嗎?看到這條日誌你能做什麼?能不能給問題排查帶來好處?
  20. 【參考】可以使用warn日誌級別來記錄用戶輸入參數錯誤的情況,避免用戶投訴時,無所適從。注意日誌輸出的級別,error級別隻記錄係統邏輯出錯、異常等重要的錯誤信息。如非必要,請不要在此場景打出error級別。

最後更新:2017-04-01 17:00:39

  上一篇:go ODPS JOB 長尾問題調優
  下一篇:go 網站頁麵HTML代碼優化需要注意的問題