關於異常,在排查問題的時候,日誌對於我們顯的格外重要,前幾天發布的時候就遇到一個悲劇的現象,發布beta測試的時候,發現有個NullPointException,但是日誌中只有日誌的類全名稱,但是沒有棧資訊以及程式碼號,無法定位哪一行跑出來的,於是只能重新改代碼,十分之蛋疼。於是把日誌相關的東西掃一下雷。
---------------------------------------------------------------------------------------------------------------------
日誌系統
Log4j —— 較早出現的比較成功的日誌系統是Log4j。Log4j開創的日誌系統模型(Logger/Appender/Level)行之有效,並一直延用至今。
JUL(java.util.logging.*) —— JDK1.4是第一個內建日誌系統的JDK,簡稱(JUL)。JUL並沒有明顯的優勢來戰勝Log4j,反而造成了標準的混亂 —— 採用不同日誌系統的應用程式無法和諧共存。
Logback —— 是較新的日誌系統。它是Log4j的作者吸取多年的經驗教訓以後重新做出的一套系統。它的使用更方便,功能更強,而且效能也更高。Logback不能單獨使用,必須配合日誌架構SLF4J來使用。
-----------------------------------------------------------------------------------------------------------------------
引用一篇文章中關於java log的描述。
為了克服多種日誌系統並存所帶來的混亂,就出現了“日誌架構”。日誌架構本身不提供記錄日誌的功能,它只提供了日誌調用的介面。基本可以理解,log4j作為log資訊的配置,就是日誌打在哪裡,怎麼打等,然後有開源jar包提供了訪問記錄檔的介面,用來和具體的日誌系統解耦,例如commmon-logging和slf4j,提供了LogFactory來擷取具體的log對象,擷取了log對象後,就可以打日誌了。下面是兩個常用的日誌架構
(1)commons-logging,apache提供的日誌門面介面,主要是為了避免程式和具體的log耦合。首先需要引入commons-logging.jar(通過maven或者其他方法皆可),然後這樣使用就可以了
<!--[if !supportLists]-->1. <!--[endif]-->Log logger = LogFactory.getLog(TestCommonsLogging.class);
<!--[if !supportLists]-->2. <!--[endif]-->logger.debug(“test debug”);
(2)slf4j和commons-logging功能類似,首先要引入slf4j-api-1.6.4jar,然後比如要支援log4j,就引入slf4j-log4j12-1.6.4jar
Logger logger = LoggerFactory.getLogger(Slf4jTest.class);
logger.debug(“test debug”); <!--EndFragment-->
-----------------------------------------------------------------------------------------------------------------------
Log4j的幾個概念:
(1)layout:一行日誌的格式,正則形式
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以靈活地指定配置模式),
%p: 輸出日誌資訊優先順序,即DEBUG,INFO,WARN,ERROR,FATAL,
%d: 輸出日誌時間點的日期或時間,預設格式為ISO8601,也可以在其後指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},
%r: 輸出自應用啟動到輸出該log資訊耗費的毫秒數
%c: 輸出日誌資訊所屬的類目,通常就是所在類的全名
%t: 輸出產生該日誌事件的線程名
%l: 輸出日誌事件的發生位置,
%x: 輸出和當前線程相關聯的NDC(嵌套診斷環境),尤其用到像java servlets這樣的多客戶多線程的應用中。
%%: 輸出一個"%"字元
%F: 輸出日誌訊息產生時所在的檔案名稱
%L: 輸出代碼中的行號
%m: 輸出代碼中指定的訊息,產生的日誌具體資訊
%n: 輸出一個斷行符號分行符號,
org.apache.log4j.SimpleLayout(包含日誌資訊的層級和資訊字串),
org.apache.log4j.TTCCLayout(包含日誌產生的時間、線程、類別等等資訊)
如果需要線程資訊,可以加上%t來擷取這個資訊
(2)appender:輸出日誌的位置資訊
org.apache.log4j.ConsoleAppender(控制台),
org.apache.log4j.FileAppender(檔案),
org.apache.log4j.DailyRollingFileAppender(每天產生一個記錄檔),
org.apache.log4j.RollingFileAppender(檔案大小到達指定尺寸的時候產生一個新的檔案),
org.apache.log4j.WriterAppender(將日誌資訊以流格式發送到任意指定的地方)
(3)level:日誌輸出等級,共五級,DEBUG/INFO/WARN/ERROR/FATAL
-----------------------------------------------------------------------------------------------------------------------
日誌中沒有異常棧資訊:
JDK5之後JVM有個最佳化,就是如果一個異常拋出一段時間後,為了效能考慮,後續就不會再拋出stackTrace資訊,當然這個功能也可以通過JVM的啟動參數來去掉:-XX:-OmitStackTraceInFastThrow.
-----------------------------------------------------------------------------------------------------------------------
關於Throwable
(1)getCause()
擷取這個拋出的原因;
(2)getMessage()
擷取這個拋出的描述資訊,例如throw new Exception("我是拋出來的異常"),
這時候次方法會返回括弧裡面的文本資訊
(3)getStackTrace()
擷取拋出的棧資訊,printStackTrace()能夠列印棧資訊。
logger.error("i am zhongyong",e); 輸出文本資訊以及異常棧資訊
logger.error(e); 只是返回異常的名稱資訊,棧資訊沒有
-----------------------------------------------------------------------------------------------------------------------
異常轉型在上面已經提到過了,實際上就是捕獲到異常後,將異常以新的類型的異常再拋出,這樣做一般為了異常的資訊更直觀。比如:
public void run() throws MyException{
...
try{
...
}catch(IOException e){
...
throw new MyException();
}finally{
...
}
}
在throw new MyException()時候,建議MyException(e),否則就會出現異常被吃掉的情況。
-----------------------------------------------------------------------------------------------------------------------
對於運行時異常,我們不要用try...catch來捕獲處理,而是在程式開發調試階段,盡量去避免這種異常,一旦發現該異常,正確的做法就會改進程式設計的代碼和實現方式,修改程式中的錯誤,從而避免這種異常。捕獲並處理運行時異常是好的解決辦法,因為可以通過改進代碼實現來避免該種異常的發生。
對於受檢查異常,沒說的,老老實實去按照異常處理的方法去處理,要麼用try...catch捕獲並解決,要麼用throws拋出。
對於Error(執行階段錯誤),不需要在程式中做任何處理,出現問題後,應該在程式在外的地方找問題,然後解決。
1、避免過大的try塊,不要把不會出現異常的代碼放到try塊裡面,盡量保持一個try塊對應一個或多個異常。
2、細化異常的類型,不要不管什麼類型的異常都寫成Excetpion。
3、catch塊盡量保持一個塊捕獲一類異常,不要忽略捕獲的異常,捕獲到後要麼處理,要麼轉譯,要麼重新拋出新類型的異常。
4、不要把自己能處理的異常拋給別人。
5、不要用try...catch參與控製程序流程,異常控制的根本目的是處理常式的非正常情況。
-----------------------------------------------------------------------------------------------------------------------