《阿里巴巴Java開發手冊(正式版)》--異常日誌

衣舞晨風發表於2017-02-21

(一)異常處理

1.【強制】不要捕獲 Java類庫中定義的繼承自 RuntimeException的執行時異常類,如:IndexOutOfBoundsException / NullPointerException,這類異常由程式設計師預檢查來規避,保證程式健壯性。
正例:

if (obj != null) {...
}

反例:

try {
    obj.method()
} catch(NullPointerException e) {...
}

2.【強制】異常不要用來做流程控制,條件控制,因為異常的處理效率比條件分支低。
3.【強制】對大段程式碼進行 try-catch,這是不負責任的表現。catch時請分清穩定程式碼和非穩定程式碼,穩定程式碼指的是無論如何不會出錯的程式碼。對於非穩定程式碼的 catch儘可能進行區分異常型別,再做對應的異常處理。
4.【強制】捕獲異常是為了處理它,不要捕獲了卻什麼都不處理而拋棄之,如果不想處理它,請將該異常拋給它的呼叫者。最外層的業務使用者,必須處理異常,將其轉化為使用者可以理解的內容。
5.【強制】有 try塊放到了事務程式碼中,catch異常後,如果需要回滾事務,一定要注意手動回滾事務。
6.【強制】finally塊必須對資源物件、流物件進行關閉,有異常也要做 try-catch。
說明:如果 JDK7,可以使用 try-with-resources方式。
7.【強制】不能在 finally塊中使用 return,finally塊中的 return返回後方法結束執行,不會再執行 try塊中的 return語句。
8.【強制】捕獲異常與拋異常,必須是完全匹配,或者捕獲異常是拋異常的父類。
說明:如果預期對方拋的是繡球,實際接到的是鉛球,就會產生意外情況。
9.【推薦】方法的返回值可以為 null,不強制返回空集合,或者空物件等,必須新增註釋充分說明什麼情況下會返回 null值。呼叫方需要進行 null判斷防止 NPE問題。
說明:本規約明確防止 NPE是呼叫者的責任。即使被呼叫方法返回空集合或者空物件,對呼叫者來說,也並非高枕無憂,必須考慮到遠端呼叫失敗,執行時異常等場景返回 null的情況。
10.【推薦】防止 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。
11.【推薦】在程式碼中使用“拋異常”還是“返回錯誤碼”,對於公司外的 http/api開放介面必須使用“錯誤碼”;而應用內部推薦異常丟擲;跨應用間 RPC呼叫優先考慮使用 Result方式,封裝 isSuccess、“錯誤碼”、“錯誤簡簡訊息”。
說明:關於 RPC方法返回方式使用 Result方式的理由:
1)使用拋異常返回方式,呼叫方如果沒有捕獲到就會產生執行時錯誤。
2)如果不加棧資訊,只是 new自定義異常,加入自己的理解的 error message,對於呼叫端解決問題的幫助不會太多。如果加了棧資訊,在頻繁呼叫出錯的情況下,資料序列化和傳輸的效能損耗也是問題。
12.【推薦】定義時區分 unchecked/checked 異常,避免直接使用 RuntimeException丟擲,更不允許丟擲 Exception或者 Throwable,應使用有業務含義的自定義異常。推薦業界已定義過的自定義異常,如:DAOException / ServiceException等。
13.【參考】避免出現重複的程式碼(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(Abc.class);

2.【強制】日誌檔案推薦至少儲存 15天,因為有些異常具備以“周”為頻次發生的特點。
3.【強制】應用中的擴充套件日誌(如打點、臨時監控、訪問日誌等)命名方式:
appName_logType_logName.log。logType:日誌型別,推薦分類有
stats/desc/monitor/visit等;logName:日誌描述。這種命名的好處:通過檔名就可知道日誌檔案屬於什麼應用,什麼型別,什麼目的,也有利於歸類查詢。
正例:mppserver應用中單獨監控時區轉換異常,如:

mppserver_monitor_timeZoneConvert.log

說明:推薦對日誌進行分類,錯誤日誌和業務日誌儘量分開存放,便於開發人員檢視,也便於通過日誌對系統進行及時監控。
4.【強制】對 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);

5.【強制】避免重複列印日誌,浪費磁碟空間,務必在 log4j.xml中設定 additivity=false。
正例:

<logger name="com.taobao.dubbo.config" additivity="false">

6.【強制】異常資訊應該包括兩類資訊:案發現場資訊和異常堆疊資訊。如果不處理,那麼往上拋。
正例:

logger.error(各類引數或者物件 toString + "_" + e.getMessage(), e);

7.【推薦】可以使用 warn日誌級別來記錄使用者輸入引數錯誤的情況,避免使用者投訴時,無所適從。注意日誌輸出的級別,error級別只記錄系統邏輯出錯、異常等重要的錯誤資訊。如非必要,請不要在此場景打出 error級別。
8.【推薦】謹慎地記錄日誌。生產環境禁止輸出 debug日誌;有選擇地輸出 info日誌;如果使用 warn來記錄剛上線時的業務行為資訊,一定要注意日誌輸出量的問題,避免把伺服器磁碟撐爆,並記得及時刪除這些觀察日誌。
說明:大量地輸出無效日誌,不利於系統效能提升,也不利於快速定位錯誤點。記錄日誌時請思考:這些日誌真的有人看嗎?看到這條日誌你能做什麼?能不能給問題排查帶來好處?

本文整理自:《阿里巴巴Java開發手冊(正式版)》

整理本文的目的是方便自己閱讀,標註等,如有侵權,立馬刪除。

作者:jiankunking 出處:http://blog.csdn.net/jiankunking

相關文章