java開發中常見的幾種日志管理方案有以下4種:
1. Commons-logging + log4j
2. log4j
3. slf4j + log4j + commmons-logging
4. slf4j + log4j
詳細說明如下。
1、log4j
- 概述
log4j是Apache的一個開源項目,主要是用來做Java開發中的日志管理工作。主要是由三個重要組件構成的。可管理日志的優先級、輸出目的地以及輸出格式等。它的配置文件主要有XML和properties兩種,當然,也可以在程序里配置,但實際開發中一般使用properties文件。
- log4j的組件
1.1、日志信息的優先級(Level)
有7個日志級別:OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL,級別從做到有一次降低。
Off:關閉所有的日志記錄
Fatal:記錄嚴重的錯誤,並且會導致應用程序退出
Error:記錄嚴重的錯誤,但不會影響程序的繼續運行
Warn:記錄警告
Info:記錄程序中比較有意義的信息
Debug:記錄程序中的細節信息
All:記錄所有的日志
1.2、日志信息的輸出目的地(Appender)
log4j可以把日志傳送到控制台、文件、GUI組件、甚至是套接口服務器、NT的事件記錄器、UNIX Syslog守護進程等。
常用的有以下幾種:
org.apache.log4j.ConsoleAppender(控制台)
org.apache.log4j.FileAppender(文件)
org.apache.log4j.DailyRollingFileAppender(每天產生一個日志文件)
org.apache.log4j.RollingFileAppender(文件大小到達指定尺寸的時候產生新文件)
org.apache.log4j.WriterAppender(將日志信息以流格式發送到任意指定的地方)
org.apache.log4j.jdbc.JDBCAppender(將日志信息寫到數據庫里)
1.3、日志信息的輸出格式(Layout)
輸出格式主要有下邊幾種:
org.apache.log4j.HTMLLayout(HTML表格形式)
org.apache.log4j.PatternLayout(通過表達式指定格式)
org.apache.log4j.SimpleLayout(僅僅包含日志信息的級別和信息的簡單字符串)
org.apache.log4j.TTCCLayout(包含日志產生的時間、線程、類別等等信息)
在使用表達式指定格式的時候,表達式和C的風格還是很相近的,參數如下:
%m 輸出代碼中指定的消息 %p 輸出優先級,即DEBUG,INFO,WARN,ERROR,FATAL %r 輸出自應用啟動到輸出該log信息耗費的毫秒數 %c 輸出所屬的類目,通常就是所在類的全名 %t 輸出產生該日志事件的線程名 %n 輸出一個回車換行符,Windows平台為“rn”,Unix平台為“n” %d 輸出日志時間點的日期或時間,默認格式為ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},輸出類似:2002年10月18日 22:10:28,921 %l 輸出日志事件的發生位置,包括類目名、發生的線程,以及在代碼中的行數。舉例:Testlog4.main(TestLog4.java:10) %x: 輸出和當前線程相關聯的NDC(嵌套診斷環境),尤其用到像java servlets這樣的多客戶多線程的應用中。 %%: 輸出一個”%”字符 %F: 輸出日志消息產生時所在的文件名稱 %L: 輸出代碼中的行號 %m: 輸出代碼中指定的消息,產生的日志具體信息 %n: 輸出一個回車換行符,Windows平台為”\r\n”,Unix平台為”\n”輸出日志信息換行 可以在%與模式字符之間加上修飾符來控制其最小寬度、最大寬度、和文本的對齊方式。 如: 1)%20c:指定輸出category的名稱,最小的寬度是20,如果category的名稱小於20的話,默認的情況下右對齊。 2)%-20c:指定輸出category的名稱,最小的寬度是20,如果category的名稱小於20的話,”-”號指定左對齊。 3)%.30c:指定輸出category的名稱,最大的寬度是30,如果category的名稱大於30的話,就會將左邊多出的字符截掉,但小於30的話也不會有空格。 4)%20.30c:如果category的名稱小於20就補空格,並且右對齊,如果其名稱長於30字符,就從左邊交遠銷出的字符截掉。
1.4、配置文件格式(properties)
日志級別設置:
log4j.rootLogger = [ level ] , appenderName, appenderName
level:OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL
輸出的目的地:
#定義appenderName輸出到控制器 log4j.appender.appenderName=org.apache.log4j.ConsoleAppender #定義appenderName的布局模式為PaternLayout log4j.appender.appenderName.layout=org.apache.log4j.PatternLayout
這里的appenderName和上邊日志級別里的appenderName是一樣的,也就是輸出的目的地名字,可以任意取。
輸出格式配置:
# 定義appenderName的輸出格式
log4j.appender.appenderName.layout.ConversionPattern=%4p [%t] (%F:%L) - %m%n
下邊是一個完整的配置配置文件,僅供參考:
#定義輸出端 log4j.rootCategory=INFO,A1,A2,A3 #定義A1輸出到控制器 log4j.appender.A1=org.apache.log4j.ConsoleAppender #定義A1的布局模式為PaternLayout log4j.appender.A1.layout=org.apache.log4j.PatternLayout # 定義A1的輸出格式 log4j.appender.A1.layout.ConversionPattern=%4p [%t] (%F:%L) - %m%n #定義A2輸出到文件 log4j.appender.A2=org.apache.log4j.DailyRollingFileAppender #定義A2輸出到哪個文件 log4j.appender.A2.File=D:\\log\\sysLog.log #定義A2輸出文件的最大長度 #log4j.appender.A2.MaxFileSize = 1KB #定義A2的備份文件數 #log4j.appender.A2.MaxBackupIndex = 3 #定義A2的布局模式為PatternLayout log4j.appender.A2.layout=org.apache.log4j.PatternLayout #定義A2的輸出模式 log4j.appender.A2.layout.ConversionPattern=%d{yyyy-MM-dd hh:mm:ss}:%p %t %c - %m%n #定義A3輸出到數據庫 log4j.appender.A3=org.apache.log4j.jdbc.JDBCAppender log4j.appender.A3.URL=jdbc:mysql://localhost:3306/db_log4j log4j.appender.A3.driver=com.mysql.jdbc.Driver log4j.appender.A3.user=root log4j.appender.A3.password=root #定義A3的布局和執行的SQL語句 log4j.appender.A3.layout=org.apache.log4j.PatternLayout log4j.appender.A3.layout.ConversionPattern=INSERT INTO tb_log(createDate,thread,level,class,message) values('%d','%t','%-5p','%c','%m')
下邊是一段Java的測試代碼(log4j):
import org.apache.log4j.Logger;
Logger logger = Logger.getLogger(Log4JTest.class); if(logger.isDebugEnabled()){ logger.debug(new Exception("testLog4j debug demo.").getMessage()); } if(logger.isInfoEnabled()){ logger.info(new Exception("testLog4j info demo.").getMessage()); } logger.error(new Exception("testLog4j error demo.").getMessage()); logger.fatal(new Exception("testLog4j fatal demo.").getMessage());
2、Commons logging
- 概述
Apache針對不同的開發語言做了一系列的日志工具包,在Java、.net、php、c++上都可以用,並且為這些日志制定了風格一致的操作方式,這里就實現了Commons-logging(JCL),Commons-logging主要是為哪些需要在不同環境下使用不同日志結構做開發的程序員而編寫的,其中包括Apache Log4j和Java log。使用Commons-loogging的Log接口,並且在運行時決定使用哪種日志架構。現在使用Commons-logging和Log4J已經成為了Java日志的標准解決方案。
已經可以使用Log4j了,可為什么還要使用Commons-logging呢?在這里,Commons-logging相當於一個統一的日志接口集,當然他也有已經實現的SimpleLog,但功能很弱。在運行時來選擇使用哪套日志的實現(例如Log4J),使得后期更改日志框架很方便,只需要更改JAR包就可以。另外,Commons-logging的日志管理操作更豐富、簡單。
- Commons-logging和log4j的兼容
log4j不依賴任何項目,這里點可以在log4j的官網查閱:http://logging.apache.org/log4j/2.x/dependencies.html 。
Commons-logging的在編譯依賴、測試依賴等依賴項上都依賴有相關的依賴,所以,在這里可以找到Commons-logging和哪個版本的Log4j兼容性最好(很顯然是編譯依賴的版本)。具體的依賴關系可見:http://commons.apache.org/proper/commons-logging/dependencies.html。
編譯依賴:用源碼進行編譯時需要的依賴,例如:拿到了Commons-logging的源碼,想要編譯得到Commons-logging.jar,那么就需要提供依賴中需要的那些jar包。
Commons-logging和log4j結合使用時,Java測試代碼如下(Commons-logging + log4j):
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
Log log = LogFactory.getLog(Log4JTest.class); if (log.isDebugEnabled()) { log.debug(new Exception("Log4JCommonslogging debug demo.").getMessage()); } if (log.isErrorEnabled()) { log.error(new Exception("Log4JCommonslogging error demo.").getMessage()); } if (log.isInfoEnabled()) { log.info(new Exception("Log4JCommonslogging info demo.").getMessage()); } if (log.isFatalEnabled()) { log.fatal(new Exception("Log4JCommonslogging fatal demo.").getMessage()); }
3、 SLF4J
與Apache Commons-logging類似,但是它是在編譯時靜態綁定Log庫的,而Commons-logging是動態查找的機制,是在程序運行時才找出真正使用的日志庫。
- slf4j的必要性
commons-logging是動態查找機制,程序運行時找出真正的日志庫,是使用ClassLoader來尋找、加載日志庫的。但是這樣會有個弊端,例如OSGI開發中,為保持插件間的獨立性,插件只能使用自己的ClassLoader,這就使得Apache Commons-logging無法工作。
- slf4j的優勢
-
- 日志記錄方式更優雅
如果只使用log4j,每次記錄日志的時候需要判斷當前的日志級別,例如:
if(logger.isDebugEnabled()){ logger.debug(new Exception("testLog4j debug demo.").getMessage()); }
但是在使用slf4j時只需要如下:
logger.debug("Hello world:{}",new Exception("testLog4JSLF4J debug demo.").getMessage());
slf4j底層會識別日志界別,從而不用自己去判斷,大大的簡化了書寫。
另外,提供了占位符"{}",使書寫布局更加靈活。
- 降低日志系統的對內存消耗
slf4j是延遲字符串的加載,上邊這段代碼的環境中如果日志級別高於debug的話,那么new Exception("testLog4JSLF4J debug demo.").getMessage()這段內容將不會被加載。
另外,占位符減少了日志信息中字符串的拼接,減少了內存和cpu的性能消耗。
- log4j + slf4j + commons-logging
這樣的結構體系中,還是使用了commons-logging的接口,slf4j來決定底層的實現是用的哪種日志實現的。
需要的jar包:
log4j-1.2.17.jar log4j實現庫
slf4j-api-1.7.7.jar slf4j庫
slf4j-log4j12-1.7.7.jar log4j的適配器和靜態綁定log4j底層的實現
jcl-over-slf4j-1.7.7.jar 提供Commons-logging接口,實現底層是由slf4j來決定使用哪種實現
測試代碼和Commons-logging和log4j結合的代碼樣例中的代碼一樣(slf4j + log4j + commmons-logging),更改日志框架等不用修改代碼,是不是很方便。關於包之間的兼容性,可下載slf4j包,解壓后查看里邊的pom文件。
- log4j + slf4j
這樣的結構體系中,還是使用了SLF4J的接口,使用什么樣的日志庫,由編譯時classpath下的日志庫決定。個人推薦這種方法。
需要的jar包:
log4j-1.2.17.jar log4j實現庫
slf4j-api-1.7.7.jar slf4j庫
slf4j-log4j12-1.7.7.jar log4j的適配器和靜態綁定log4j底層的實現
測試代碼如下(slf4j + log4j):
import org.slf4j.Logger; import org.slf4j.LoggerFactory; Logger logger = LoggerFactory.getLogger(SLF4JTest.class); logger.debug("some thing :{}",new Exception("testLog4JSLF4J debug demo.").getMessage()); logger.error(new Exception("testLog4JSLF4J error demo.").getMessage()); logger.info(new Exception("testLog4JSLF4J info demo.").getMessage());
以上四種Java處理日志的方法,參考文獻見:
http://www.importnew.com/7450.html
http://singleant.iteye.com/blog/934593