二、異常日志
(一)異常處理
1.【強(qiáng)制】不要捕獲Java類庫(kù)中定義的繼承自RuntimeException的運(yùn)行時(shí)異常類,如:
IndexOutOfBoundsException / NullPointerException,這類異常由程序員預(yù)檢查來(lái)規(guī)避,保
證程序健壯性。
正例:if(obj != null) {...}
反例:try { obj.method() } catch(NullPointerException e){…}
2.【強(qiáng)制】異常不要用來(lái)做流程控制,條件控制,因?yàn)楫惓5奶幚硇时葪l件分支低。
3.【強(qiáng)制】對(duì)大段代碼進(jìn)行try-catch,這是不負(fù)責(zé)任的表現(xiàn)。catch時(shí)請(qǐng)分清穩(wěn)定代碼和非穩(wěn) 定代碼,穩(wěn)定代碼指的是無(wú)論如何不會(huì)出錯(cuò)的代碼。對(duì)于非穩(wěn)定代碼的catch盡可能進(jìn)行區(qū)分
異常類型,再做對(duì)應(yīng)的異常處理。
4.【強(qiáng)制】捕獲異常是為了處理它,不要捕獲了卻什么都不處理而拋棄之,如果不想處理它,請(qǐng)
將該異常拋給它的調(diào)用者。最外層的業(yè)務(wù)使用者,必須處理異常,將其轉(zhuǎn)化為用戶可以理解的
內(nèi)容。
5.【強(qiáng)制】有try塊放到了事務(wù)代碼中,catch異常后,如果需要回滾事務(wù),一定要注意手動(dòng)回 滾事務(wù)。
6.【強(qiáng)制】finally塊必須對(duì)資源對(duì)象、流對(duì)象進(jìn)行關(guān)閉,有異常也要做try-catch。 說(shuō)明:如果JDK7,可以使用try-with-resources方法。
7.【強(qiáng)制】不能在finally塊中使用return,finally塊中的return返回后方法結(jié)束執(zhí)行,不 會(huì)再執(zhí)行try塊中的return語(yǔ)句。
8.【強(qiáng)制】捕獲異常與拋異常,必須是完全匹配,或者捕獲異常是拋異常的父類。 說(shuō)明:如果預(yù)期拋的是繡球,實(shí)際接到的是鉛球,就會(huì)產(chǎn)生意外情況。
9.【推薦】方法的返回值可以為null,不強(qiáng)制返回空集合,或者空對(duì)象等,必須添加注釋充分 說(shuō)明什么情況下會(huì)返回null值。調(diào)用方需要進(jìn)行null判斷防止NPE問(wèn)題。
說(shuō)明:本規(guī)約明確防止NPE是調(diào)用者的責(zé)任。即使被調(diào)用方法返回空集合或者空對(duì)象,對(duì)調(diào)用
者來(lái)說(shuō),也并非高枕無(wú)憂,必須考慮到遠(yuǎn)程調(diào)用失敗,運(yùn)行時(shí)異常等場(chǎng)景返回null的情況。
10.【推薦】防止NPE,是程序員的基本修養(yǎng),注意NPE產(chǎn)生的場(chǎng)景:
1) 返回類型為包裝數(shù)據(jù)類型,有可能是null,返回int值時(shí)注意判空。
阿里巴巴JAVA開發(fā)手冊(cè)
19 / 32
反例:public int f(){ return Integer對(duì)象},如果為null,自動(dòng)解箱拋NPE。
2) 數(shù)據(jù)庫(kù)的查詢結(jié)果可能為null。
3) 集合里的元素即使isNotEmpty,取出的數(shù)據(jù)元素也可能為null。
4) 遠(yuǎn)程調(diào)用返回對(duì)象,一律要求進(jìn)行NPE判斷。
5) 對(duì)于Session中獲取的數(shù)據(jù),建議NPE檢查,避免空指針。
6) 級(jí)聯(lián)調(diào)用obj.getA().getB().getC();一連串調(diào)用,易產(chǎn)生NPE。
11.【推薦】在代碼中使用“拋異常”還是“返回錯(cuò)誤碼”,對(duì)于公司外的http/api開放接口必
須使用“錯(cuò)誤碼”;而應(yīng)用內(nèi)部推薦異常拋出;跨應(yīng)用間RPC調(diào)用優(yōu)先考慮使用Result方式,
封裝isSuccess、“錯(cuò)誤碼”、“錯(cuò)誤簡(jiǎn)短信息”。
說(shuō)明:關(guān)于RPC方法返回方式使用Result方式的理由:
1)使用拋異常返回方式,調(diào)用方如果沒(méi)有捕獲到就會(huì)產(chǎn)生運(yùn)行時(shí)錯(cuò)誤。
2)如果不加棧信息,只是new自定義異常,加入自己的理解的error message,對(duì)于調(diào)用
端解決問(wèn)題的幫助不會(huì)太多。如果加了棧信息,在頻繁調(diào)用出錯(cuò)的情況下,數(shù)據(jù)序列化和傳輸
的性能損耗也是問(wèn)題。
12.【推薦】定義時(shí)區(qū)分unchecked / checked異常,避免直接使用RuntimeException拋出,更
不允許拋出Exception或者Throwable,應(yīng)使用有業(yè)務(wù)含義的自定義異常。推薦業(yè)界已定義過(guò)
的自定義異常,如:DaoException / ServiceException等。
13.【參考】避免出現(xiàn)重復(fù)的代碼(Don’t Repeat Yourself),即DRY原則。
說(shuō)明:隨意復(fù)制和粘貼代碼,必然會(huì)導(dǎo)致代碼的重復(fù),在以后需要修改時(shí),需要修改所有的副
本,容易遺漏。必要時(shí)抽取共性方法,或者抽象公共類,甚至是共用模塊。
正例:一個(gè)類中有多個(gè)public方法,都需要進(jìn)行數(shù)行相同的參數(shù)校驗(yàn)操作,這個(gè)時(shí)候請(qǐng)抽取:
private boolean checkParam(DTO dto){...} ?(二)日志規(guī)約
1.【強(qiáng)制】應(yīng)用中不可直接使用日志系統(tǒng)(Log4j、Logback)中的API,而應(yīng)依賴使用日志框架SLF4J中的API,使用門面模式的日志框架,有利于維護(hù)和各個(gè)類的日志處理方式統(tǒng)一。
import org.slf4j.Logger; ?import org.slf4j.LoggerFactory; ?private static final Logger logger = LoggerFactory.getLogger(Abc.class); ?2.【強(qiáng)制】日志文件推薦至少保存15天,因?yàn)橛行┊惓>邆湟浴爸堋睘轭l次發(fā)生的特點(diǎn)。
3.【強(qiáng)制】應(yīng)用中的擴(kuò)展日志(如打點(diǎn)、臨時(shí)監(jiān)控、訪問(wèn)日志等)命名方式:
appName_logType_logName.log。logType:日志類型,推薦分類有stats/desc/monitor/visit
等;logName:日志描述。這種命名的好處:通過(guò)文件名就可知道日志文件屬于什么應(yīng)用,什么
阿里巴巴JAVA開發(fā)手冊(cè)
20 / 32
類型,什么目的,也有利于歸類查找。
正例:mppserver應(yīng)用中單獨(dú)監(jiān)控時(shí)區(qū)轉(zhuǎn)換異常,如:
mppserver_monitor_timeZoneConvert.log
說(shuō)明:推薦對(duì)日志進(jìn)行分類,錯(cuò)誤日志和業(yè)務(wù)日志盡量分開存放,便于開發(fā)人員查看,也便于
通過(guò)日志對(duì)系統(tǒng)進(jìn)行及時(shí)監(jiān)控。
4.【強(qiáng)制】對(duì)trace/debug/info級(jí)別的日志輸出,必須使用條件輸出形式或者使用占位符的方 式。
說(shuō)明:logger.debug("Processing trade with id: " + id + " symbol: " + symbol);如果
日志級(jí)別是warn,上述日志不會(huì)打印,但是會(huì)執(zhí)行字符串拼接操作,如果symbol是對(duì)象,會(huì)
執(zhí)行toString()方法,浪費(fèi)了系統(tǒng)資源,執(zhí)行了上述操作,最終日志卻沒(méi)有打印。
正例:(條件)
if (logger.isDebugEnabled()) { ???logger.debug("Processing trade with id: " + id + " symbol: " + symbol); ??}正例:(占位符)
logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol); ?5.【強(qiáng)制】避免重復(fù)打印日志,浪費(fèi)磁盤空間,務(wù)必在log4j.xml中設(shè)置additivity=false。 正例: ?6.【強(qiáng)制】異常信息應(yīng)該包括兩類信息:案發(fā)現(xiàn)場(chǎng)信息和異常堆棧信息。如果不處理,那么往上 拋。
正例:logger.error(各類參數(shù)或者對(duì)象toString + "_" + e.getMessage(), e);
7.輸出的POJO類必須重寫toString方法,否則只輸出此對(duì)象的hashCode值(地址值),沒(méi)啥 參考意義。
8.【推薦】可以使用warn日志級(jí)別來(lái)記錄用戶輸入?yún)?shù)錯(cuò)誤的情況,避免用戶投訴時(shí),無(wú)所適
從。注意日志輸出的級(jí)別,error級(jí)別只記錄系統(tǒng)邏輯出錯(cuò)、異常、或者重要的錯(cuò)誤信息。如
非必要,請(qǐng)不要在此場(chǎng)景打出error級(jí)別,避免頻繁報(bào)警。
9.【推薦】謹(jǐn)慎地記錄日志。生產(chǎn)環(huán)境禁止輸出debug日志;有選擇地輸出info日志;如果使 用warn來(lái)記錄剛上線時(shí)的業(yè)務(wù)行為信息,一定要注意日志輸出量的問(wèn)題,避免把服務(wù)器磁盤
撐爆,并記得及時(shí)刪除這些觀察日志。
說(shuō)明:大量地輸出無(wú)效日志,不利于系統(tǒng)性能提升,也不利于快速定位錯(cuò)誤點(diǎn)。紀(jì)錄日志時(shí)請(qǐng)
思考:這些日志真的有人看嗎?看到這條日志你能做什么?能不能給問(wèn)題排查帶來(lái)好處?
10.【參考】如果日志用英文描述不清楚,推薦使用中文注釋。對(duì)于中文UTF-8的日志,在secureCRT
中,set encoding=utf-8;如果中文字符還亂碼,請(qǐng)?jiān)O(shè)置:全局>默認(rèn)的會(huì)話設(shè)置>外觀>字體>
阿里巴巴JAVA開發(fā)手冊(cè)
21 / 32
選擇字符集gb2312;如果還不行,執(zhí)行命令:set termencoding=gbk,并且直接使用中文來(lái)
進(jìn)行檢索。
ts?e??&