如何正確使用log4j?
關鍵字: 如何正確使用log4j?
Java Web開發的過程中,通過會采用輸出log的方式來進行調試,產品上線之后,也通常使用log來記錄系統的運行狀態。最簡單的輸出log的方式就是使用System.out.println(String msg)和System.err.println(String msg),然而這種方式的弊端是顯而易見的,例如:
1. 調試期輸出log的代碼,在上線運行前需要注釋掉,否則可能會影響系統性能或者造成日志文件過於龐大。當出現問題需要跟蹤的時候,又需要修改代碼,刪除注釋符以打開這些debug的log,非常不方便;
2. 所有的業務的log都會輸出到一個文件中去,查看起來很不方便。
多虧apache開源社區為所有Java開發者提供了Log4j這個非常弓雖大的log工具,把我們從 維護log的繁重工作中解脫了出來。Log4j可能通過配置文件實現多logger,每個logger可以有不同的輸出文件、輸出格式和log級別,具體 細節我就不再贅述,不了解的同學可以參考相關的文檔或者示例代碼。
這里主要總結幾點Log4j使用過程中的常見問題和注意事項,閱讀一下內容之前,下面的文章將假設你已經有一定的Log4j使用經驗了。
-
1. 獲取Logger實例的方法:
protected static final Logger logger = Logger.getLogger("logger_name");
或
private static final Logger logger = Logger.getLogger("logger_name");
還有一個獲取Logger的方法:
Logger.getLogger(Class clazz);
1.1 為什么要加“static”?
因為同一個logger name對應唯一的Logger實例,而Logger.getLogger方法在從logger容器中取logger的過程中,有一個步驟被加了同步鎖, 這就造成了如果在多線程的競爭環境中頻繁地調用getLogger操作,有可能會造成堵塞。而且這種操作是沒有價值的,所以使用static來保證需要需 用Logger的類所有的對象共享一個Logger的引用,並且只獲取一次就可以了。
1.2 為什么要加“final”
防止多logger同時使用時,Logger引用不小心被賦值,這是一個良好的編程習慣。
1.3 Logger.getLogger(String loggerName)與Logger.getLogger(Class clazz)有什么區別?
本質上是一樣的,Logger.getLogger(Class clazz)方法實際上是調用了Logger.getLogger(clazz.getName())來獲取Logger的。
1.4 log4j配置文件中沒有配置logger_name對應的logger怎么辦?
如果沒有logger_name對應的配置,則Logger.getLogger方法返回默認的logger。
-
2. Logger共有6個log級別,按優先級由低到高排序分別是:
TRACE < DEBUG < INFO < WARN < ERROR < FATAL
他們分別對應Logger的trace, debug, info, warn, error和fatal方法進行輸出,但是我們通常只使用debug、info和error這三個最常用的級別。運行時的log輸出級別可以通過更改log4j配置文件來進行配置,就免去了注釋掉debug代碼的尷尬。
-
3. 輸出log的正確方式:
if (logger.isInfoEnabled()) {
logger.info("User " + userId + " is using app " + appId);
}
3.1 為什么要加上logger.isInfoEnabled()?
直接使用logger.info("User " + userId + " is using app " + appId)來輸出log,也能夠達到log級別為INFO或在INFO以下時才輸出:("User " + userId + " is using app " + appId),因為logger.info方法內部有判斷輸出級別的代碼。但是在進入logger.info函數之前,("User " + userId + " is using app " + appId) 這個表達式已經通過運算拼接成了一個字符串;而如果事先使用 if (logger.isInfoEnabled())進行判斷,那么當log級別在INFO以上時,就能省去上述的字符串操作,在高並發和復雜log信息拼 接的情況下,使用這種標准的方法輸出log能夠省去不小的系統開銷。另外,如果構造log信息的過程需要大量字符串操作,建議使用 StringBuilder來完成字符串拼接。
3.2 ERROR及其以上級別的log信息是一定會被輸出的,所以只有logger.isDebugEnabled和logger.isInfoEnabled方法,而沒有logger.isErrorEnabled方法。
-
4. debug、info、error等輸出log的方法是線程安全的,請放心使用。
-
5. log有風險,輸出需謹慎!由於輸出log過程需要進行磁盤操作,且log4j為了保證log輸出過程的線程安全性而使用同步鎖,就使得輸出log成為很耗時的操作,所以log信息一定要言簡意賅,不要輸出一些無用的log。
以上為我個人的使用經驗,歡迎大家指教和補充。
最后,祝願log4j給大家帶來更加愉快的開發體驗。
