簡單的說,日誌就是記錄程序的運行軌跡,方便查找關鍵信息,也方便快速定位解決問題。css
咱們 Java 程序員在開發項目時都是依賴 Eclipse/ Idea 等開發工具的 Debug 調試功能來跟蹤解決 Bug,在開發環境能夠這麼作,但項目發佈到了測試、生產環境呢?你有可能會說可使用遠程調試,但實際並不能容許讓你這麼作。java
因此,日誌的做用就是在測試、生產環境沒有 Debug 調試工具時開發、測試人員定位問題的手段。日誌打得好,就能根據日誌的軌跡快速定位並解決線上問題,反之,日誌輸出很差不能定位到問題不說反而會影響系統的性能。程序員
優秀的項目都是能根據日誌定位問題的,而不是在線調試,或者半天找不到有用的日誌而抓狂…apache
log4j、Logging、commons-logging、slf4j、logback,開發的同窗對這幾個日誌相關的技術不陌生吧,爲何有這麼多日誌技術,它們都是什麼區別和聯繫呢?相信大多數人搞不清楚它們的關係,下面我將一一介紹一下,之後你們不再用傻傻分不清楚了。編程
這是 Java 自帶的日誌工具類,在 JDK 1.5 開始就已經有了,在 java.util.logging
包下。設計模式
Log4j 是 Apache 的一個開源日誌框架,也是市場佔有率最多的一個框架。大多數沒用過 Java Logging, 但沒人敢說沒用過 Log4j 吧,反正從我接觸 Java 開始就是這種狀況,作 Java 項目必有 Log4j 日誌框架。tomcat
注意:log4j 在 2015/08/05 這一天被 Apache 宣佈中止維護了,用戶須要切換到 Log4j2上面去。框架
上面介紹的 log4j 是一個具體的日誌框架的實現,而 commons-logging 就是日誌的門面接口,它也是 apache 最先提供的日誌門面接口,用戶能夠根據喜愛選擇不一樣的日誌實現框架,而沒必要改動日誌定義,這就是日誌門面的好處,符合面對接口抽象編程。工具
全稱:Simple Logging Facade for Java,即簡單日誌門面接口,和 Apache 的 commons-logging 是同樣的概念,它們都不是具體的日誌框架,你能夠指定其餘主流的日誌實現框架。性能
Slf4j 也是如今主流的日誌門面框架,使用 Slf4j 能夠很靈活的使用佔位符進行參數佔位,簡化代碼,擁有更好的可讀性,這個後面會講到。
Logback 是 Slf4j
的原生實現框架,一樣也是出自 Log4j
一我的之手,但擁有比 log4j
更多的優勢、特性和更作強的性能,如今基本都用來代替 log4j
成爲主流。
爲何 Logback 會成爲主流?
不管從設計上仍是實現上,Logback相對log4j而言有了相對多的改進。不過儘管難以一一細數,這裏仍是列舉部分理由爲何選擇logback而不是log4j。牢記logback與log4j在概念上面是很類似的,它們都是有同一羣開發者創建。因此若是你已經對log4j很熟悉,你也能夠很快上手logback。若是你喜歡使用log4j,你也許會迷上使用logback。
更快的執行速度
基於咱們先前在log4j上的工做,logback 重寫了內部的實現,在某些特定的場景上面,甚至能夠比以前的速度快上10倍。在保證logback的組件更加快速的同時,同時所需的內存更加少。
commons-loggin
、slf4j
只是一種日誌抽象門面,不是具體的日誌框架。log4j
、logback
是具體的日誌實現框架。slf4j + logback
。固然也可使用slf4j + log4j
、commons-logging + log4j
這兩種日誌組合框架。從上圖能夠看出 slf4j
很強大吧,不但能和各類日誌框架對接,還能和日誌門面 commons-logging
進行融合。
日誌的輸出都是分級別的,不一樣的設置不一樣的場合打印不一樣的日誌。下面拿最廣泛用的 Log4j
日誌框架來作個日誌級別的說明,這個也比較奇全,其餘的日誌框架也都大同小異。
Log4j 的級別類 org.apache.log4j.Level
裏面定義了日誌級別,日誌輸出優先級由高到底分別爲如下8種。
日誌級別 | 描述 |
---|---|
OFF | 關閉:最高級別,不輸出日誌。 |
FATAL | 致命:輸出很是嚴重的可能會致使應用程序終止的錯誤。 |
ERROR | 錯誤:輸出錯誤,但應用還能繼續運行。 |
WARN | 警告:輸出可能潛在的危險情況。 |
INFO | 信息:輸出應用運行過程的詳細信息。 |
DEBUG | 調試:輸出更細緻的對調試應用有用的信息。 |
TRACE | 跟蹤:輸出更細緻的程序運行軌跡。 |
ALL | 全部:輸出全部級別信息。 |
因此,日誌級別優先級標準順序爲:
ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF,all優先級最小,off優先級最大。
若是日誌設置爲 L
,一個級別爲 P
的輸出日誌只有當 P >= L
時日誌纔會輸出。
即若是日誌級別 L 設置 INFO,只有 P 的輸出級別爲 INFO、WARN,後面的日誌纔會正常輸出。
最開始也說過了,日誌不能亂打,否則起不到日誌本應該起到的做用不說,還會形成系統的負擔。在 BAT、華爲一些大公司都是對日誌規範有要求的,何時該打什麼日誌都是有規範的。
private static final Logger LOG = LoggerFactory.getLogger(this.getClass());
一般一個類只有一個 LOG 對象,若是有父類能夠將 LOG 定義在父類中。
日誌變量類型定義爲門面接口(如 slf4j 的 Logger),實現類能夠是 Log4j
、Logback
等日誌實現框架,不要把實現類定義爲變量類型,不然日誌切換不方便,也不符合抽象編程思想。
{}
佔位,[]
進行參數隔離LOG.debug("Save order with order no:[{}], and order amount:[{}]");
這種可讀性好,這樣一看就知道[]
裏面是輸出的動態參數,{}
用來佔位相似綁定變量,並且只有真正準備打印的時候纔會處理參數,方便定位問題。
若是日誌框架不支持參數化形式,且日誌輸出時不支持該日誌級別時會致使對象冗餘建立,浪費內存,此時就須要使用 isXXEnabled
判斷,如:
if(LOG.isDebugEnabled()){
// 若是日誌不支持參數化形式,debug又沒開啓,那字符串拼接就是無用的代碼拼接,影響系統性能
logger.debug("Save order with order no:" + orderNo + ", and order amount:" + orderAmount);
}
至少 debug
級別是須要開啓判斷的,線上日誌級別至少應該是 info
以上的。
這裏推薦你們用 SLF4J
的門面接口,能夠用參數化形式輸出日誌,debug
級別也沒必要用 if
判斷,簡化代碼。
項目中最經常使用有日誌級別是ERROR
、WARN
、INFO
、DEBUG
四種了,這四個都有怎樣的應用場景呢。
通常用來記錄程序中發生的任何異常錯誤信息(Throwable),或者是記錄業務邏輯出錯。
通常用來記錄一些用戶輸入參數錯誤、
這個也是平時用的最低的,也是默認的日誌級別,用來記錄程序運行中的一些有用的信息。如程序運行開始、結束、耗時、重要參數等信息,須要注意有選擇性的有意義的輸出,到時候本身找問題看一堆日誌卻找不到關鍵日誌就沒意義了。
這個級別通常記錄一些運行中的中間參數信息,只容許在開發環境開啓,選擇性在測試環境開啓。
System.out.print..
輸出日誌的時候只能經過日誌框架來輸出日誌,而不能使用 System.out.print..
來打印日誌,這個只會打印到 tomcat
控制檯,而不會記錄到日誌文件中,不方便管理日誌,若是經過服務形式啓動把日誌丟棄了那更是找不到日誌了。
e.printStackTrace()
首先來看看它的源碼:
public void printStackTrace() {
printStackTrace(System.err);
}
它其實也是利用 System.err
輸出到了 tomcat
控制檯。
如捕獲異常後又拋出了自定義業務異常,此時無需記錄錯誤日誌,由最終捕獲方進行異常處理。不能又拋出異常,又打印錯誤日誌,否則會形成重複輸出日誌。
try {
// ...
} catch (Exception e) {
// 錯誤
LOG.error("xxx", e);
throw new RuntimeException();
}
InterfaceImpl interface = new InterfaceImpl();
這段代碼你們都看得懂吧?應該面向接口的對象編程,而不是面向實現,這也是軟件設計模式的原則,正確的作法應該是。
Interface interface = new InterfaceImpl();
日誌框架裏面也是如此,上面也說了,日誌有門面接口,有具體實現的實現框架,因此你們不要面向實現編程。
看如下代碼,這樣不會記錄詳細的堆棧異常信息,只會記錄錯誤基本描述信息,不利於排查問題。
try {
// ...
} catch (Exception e) {
// 錯誤
LOG.error('XX 發生異常', e.getMessage());
// 正確
LOG.error('XX 發生異常', e);
}
try {
// ...
} catch (Exception e) {
// 錯誤
LOG.info("XX 發生異常...", e);
}
你們看出了什麼問題嗎?用 info
記錄 error
日誌,日誌輸出到了 info
日誌文件中了,同事拼命地在 error
錯誤日誌文件裏面找怎麼能找到呢?
這個是什麼意思,若是你的框架使用了性能不高的 Log4j
框架,那就不要在上千個 for
循環中打印日誌,這樣可能會拖垮你的應用程序,若是你的程序響應時間變慢,那要考慮是否是日誌打印的過多了。
for(int i=0; i<2000; i++){
LOG.info("XX");
}
最好的辦法是在循環中記錄要點,在循環外面總結打印出來。
debug
這是最後一點,也是最重要的一點。
一是由於項目自己 debug
日誌太多,二是各類框架中也大量使用 debug
的日誌,線上開啓 debug
不久就會打滿磁盤,影響業務系統的正常運行。
轉自:https://blog.csdn.net/juncle113/article/details/80973547