由于自己工作性質(zhì)發(fā)生了些變化,人隨境遷,回首時(shí),發(fā)現(xiàn)真的是歲月如梭,時(shí)光如水。彈指間,從研發(fā)到維護(hù),從總部到出差已經(jīng)是四月有余了。
在維護(hù)系統(tǒng)時(shí),難免會(huì)時(shí)不時(shí)地跟代碼再次打些交道。今天想再操刀做個(gè)小工具時(shí),居然不太記得了log4j的配置,不得不從apache再重新down了相關(guān)的jar與DOC,匆匆看看了一下,快點(diǎn)寫下來(lái),日后肯定還用得著,同時(shí)也希望對(duì)網(wǎng)友們有用。
Log4j基本上已經(jīng)是java里的首選日志工具了,它主要由三部分組成:Loggers, Appenders和Layouts (注意后面都加了s啦,顧名思義一個(gè)配置中可以分別允許有多個(gè)此類對(duì)象存在,后面將詳細(xì)介紹)。
Loggers-用來(lái)定義日志消息的類型及級(jí)別;
Appenders-用來(lái)定義日志消息的輸出終端;
Layouts-用來(lái)定義日志消息的輸出格式。
Logger
Loggers層次:
對(duì)logger的名字是大小寫敏感。
規(guī)則-如果類P的名字是另一個(gè)類C的名字的前綴,且P與C之間以“.”號(hào)連接起來(lái),那么稱P為祖先層次;如果層次A與其子層次之間沒有任何父層次,則認(rèn)為層次A為父層次。 |
例如,org.apache.log4j是org.apache.log4j.Logger的父層次;而org.apache是org.apache.log4j與org.apache.log4j.Logger的祖先層次。
另外,注意log4j中有個(gè)默認(rèn)的root級(jí)別的logger,在所有logger中,它是最高級(jí)別,其它所有logger均繼承于root,root有以下二個(gè)特性:
1. 它總是存在的。
2. 它不能通過名字直接獲取其實(shí)例(root實(shí)例可以通過類Logger的靜態(tài)方法Logger.getRootLogger獲得,而其它logger則可以直接通過名字來(lái)獲取Logger.getLogger)。
關(guān)于Loggers中的級(jí)別:
對(duì)每個(gè)logger,可以指定其級(jí)別,在系統(tǒng)org.apache.log4j.Level中,已經(jīng)定義了五個(gè)級(jí)別,分別為debug, info, warn, error, fatal。
規(guī)則-設(shè)當(dāng)前logger為X,從X開始往X的父類方向開始算(包括X本身),直到名為root的logger,第一個(gè)不為null的級(jí)別值就是X的級(jí)別值。 |
日志顯示:
級(jí)別的定義是為了過濾性地選擇日志。
規(guī)則-若當(dāng)前請(qǐng)方式級(jí)別為P,而當(dāng)前的logger的級(jí)別為Q,當(dāng)且僅當(dāng)在P>=Q的情況下,日志信息才能顯示。 |
關(guān)于日志中級(jí)別的關(guān)系為:DEBUG < INFO < WARN < ERROR < FATAL。
關(guān)于此規(guī)則的說(shuō)明,有以下代碼為實(shí)例:
// get a logger instance named "com.foo" Logger logger = Logger.getLogger("com.foo"); // Now set its level. Normally you do not need to set the // level of a logger programmatically. This is usually done // in configuration files. logger.setLevel(Level.INFO); Logger barlogger = Logger.getLogger("com.foo.Bar"); // This request is enabled, because WARN >= INFO. logger.warn("Low fuel level."); // This request is disabled, because DEBUG < INFO. logger.debug("Starting search for nearest gas station."); // The logger instance barlogger, named "com.foo.Bar", // will inherit its level from the logger named // "com.foo" Thus, the following request is enabled // because INFO >= INFO. barlogger.info("Located nearest gas station."); // This request is disabled, because DEBUG < INFO. barlogger.debug("Exiting gas station search"); |
Appenders與Layouts
Appender正如前面所述,是用來(lái)定義日志信息的輸出終端,最覺的輸出終端有console與file了,另外還有其它如GUI components, JMS, NT Event Loggers, remote socket servers等等。
Appender也有類似繼承的原則,即當(dāng)前logger的appender包括其它父類的appender。這樣就會(huì)出現(xiàn)一個(gè)logger可能擁有多個(gè)appender了,在現(xiàn)實(shí)中看來(lái),就是log4j的日志信息可以同進(jìn)輸出到console, file等等終端了。當(dāng)然,為了不使此appender惡性疊加,可以通過設(shè)置additivity標(biāo)志來(lái)阻止繼承。
規(guī)則-若當(dāng)前logger為C,則C擁有包括其自己及其父類的所有appender。另外,若C的父logger為P,且P的additivity標(biāo)志已經(jīng)設(shè)置成為false,則C只擁有自己及P的appender了,而P則只能擁有本身的appender。 |
以下表格可以清晰說(shuō)明此規(guī)則:
Logger |
Added |
Additivity |
Output Targets |
Comment |
root |
A1 |
not applicable |
A1 |
The root logger is anonymous but can be accessed with the Logger.getRootLogger() method. There is no default appender attached to root. |
x |
A-x1, A-x2 |
true |
A1, A-x1, A-x2 |
Appenders of "x" and root. |
x.y |
none |
true |
A1, A-x1, A-x2 |
Appenders of "x" and root. |
x.y.z |
A-xyz1 |
true |
A1, A-x1, A-x2, A-xyz1 |
Appenders in "x.y.z", "x" and root. |
security |
A-sec |
false |
A-sec |
No appender accumulation since the additivity flag is set to false. |
security.access |
none |
true |
A-sec |
Only appenders of "security" because the additivity flag in "security" is set to false. |
對(duì)于layout,也正如前面所述,是用來(lái)定義日志信息的輸出格式,它的與C語(yǔ)言中printf函數(shù)的格式定義基本相似,本人對(duì)printf中的格式參數(shù)不太熟悉,總感覺有些復(fù)雜,一般是平時(shí)看到自己認(rèn)為有用的格式就記一下,到時(shí)直接搬過來(lái)。如
log4j.appender.R.layout.ConversionPattern=--->%-d{yyyy-MM-dd HH:mm:ss} [%5p]%l - %m%n
時(shí),日志信息格式為:
--->2008-07-11 01:13:40 [ INFO]com.test.Log4jTest.main(Log4jTest.java:27) - Exiting application.
s 好啦,現(xiàn)在log4j的理論很膚淺地扯了一下,現(xiàn)在可以開始配置了。
配置:
Log4j的配置可以通過加載Java的properties配置文件或者XML文件來(lái)完成。
Log4j默認(rèn)的配置為,通過讀取系統(tǒng)變量log4j.configuration來(lái)找到配置文件,當(dāng)然變量的默認(rèn)值為log4j.properties,所以若沒有設(shè)置此系統(tǒng)變量,可以直接將配置文件命名為log4j.properties,然后放到類路徑下即可。另外,若想引用通過其它配置文件,則可以通過
Loader.getResource(java.lang.String)來(lái)讀取指定的配置文件。
其它:
1. 由于有了log4j中的級(jí)別繼承機(jī)制,所以可以很方便地過濾信息了,不僅可以很方便地限制日志的輸出量,也可以同時(shí)將日志輸出到不同的終端。
另外,因?yàn)樵?span lang="EN-US">java文件中,文件的物理層次關(guān)系也是直接通過“.”符號(hào)來(lái)控制的,且在很在程度上這個(gè)物理層次也決定了文件的邏輯層次,所以我們?cè)诋?dāng)前文件中獲取logger時(shí),可以直接通過當(dāng)前文件的類名來(lái)獲取,如:
static Logger logger = Logger.getLogger(MyApp.class);
這樣,在過濾消息時(shí)就很簡(jiǎn)單了,簡(jiǎn)單示例如下:
在配置文件中有
log4j.rootLogger=ERROR,stdout,R
log4j.category.com.db=DEBUG
log4j.category.com.i18n=INFO
log4j.category.com.zyx=fatal
默認(rèn)的root logger的日志級(jí)別為Error,根據(jù)級(jí)別關(guān)系這個(gè)級(jí)別也相當(dāng)高了,這樣可以減少系統(tǒng)中日志的輸出量,但有些地方可能得輸出更詳細(xì)信息,如數(shù)據(jù)庫(kù)部分,所以可以將com.db設(shè)置成了debug。另外,我在com.zyx下,我只想看到類型為fatal的日志,也可以如上所設(shè)。
2. log4j.rootLogger=ERROR,stdout, ROLLING_FILE這個(gè)定義表示root logger的日志級(jí)別為Error,后面的stdout, ROLLING_FILE表示此root有二個(gè)appender,通常可以通過這樣來(lái)定義日志可以同時(shí)向多個(gè)終端輸出,因?yàn)樽?span lang="EN-US">logger可以繼承所有父logger的appender.
如我可以通過以下定義將日志同時(shí)往console及file輸出:
log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=--->%-d{yyyy-MM-dd HH:mm:ss} [%5p]%l - %m%n log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender log4j.appender.ROLLING_FILE.File=myapp.log log4j.appender.ROLLING_FILE.Append=true log4j.appender.ROLLING_FILE.MaxFileSize=1024KB log4j.appender.ROLLING_FILE.MaxBackupIndex=10 log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout log4j.appender.ROLLING_FILE.layout.ConversionPattern==[slf5s.start]%d{DATE}[slf5s.DATE]%n\ %p[slf5s.PRIORITY]%n%x[slf5s.NDC]%n%t[slf5s.THREAD]%n\ %c[slf5s.CATEGORY]%n%l[slf5s.LOCATION]%n%m[slf5s.MESSAGE]%n%n |
實(shí)例
log4j.properties配置文件:
#defineroot logger and it's appdenders log4j.rootLogger=debug,R,stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout #Pattern to output the caller's file name and line number. log4j.appender.stdout.layout.ConversionPattern=%5p[%t](%F:%L)-%m%n log4j.appender.R=org.apache.log4j.RollingFileAppender log4j.appender.R.File=myapp.log log4j.category.R=error log4j.appender.R.MaxFileSize=100KB #Keep one backup file log4j.appender.R.MaxBackupIndex=1 log4j.appender.R.layout=org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern=%p%t%c-%m%n log4j.appender.R.layout=org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern=--->%-d{yyyy-MM-ddHH:mm:ss}[%5p]%l-%m%n |
Java測(cè)試文件:
packagecom.zyx.test; importorg.apache.log4j.Logger; publicclassLog4jTest { staticLoggerlogger= Logger.getLogger(Log4jTest.class.getName()); publicstaticvoidmain(String[] args) { logger.debug("Welcometo by blog blgjava.net/jkallen"); logger.info("Exitingapplication."); System.out.print("over"); } } |
運(yùn)行后控制臺(tái)輸出:
DEBUG [main](Log4jTest.java:9) - Welcome to by blogblgjava.net/jkallen INFO [main] (Log4jTest.java:10) - Exitingapplication. over |
運(yùn)行后日志文件輸出:
--->2008-07-1115:46:24 [DEBUG]com.test.Log4jTest.main(Log4jTest.java:9) - Welcome to by blogblgjava.net/jkallen --->2008-07-11 15:46:24 [INFO]com.test.Log4jTest.main(Log4jTest.java:10) - Exitingapplication. |
馬馬虎虎總結(jié)了一下,但也花費(fèi)了近三個(gè)小時(shí),支持下!
參考:log4j DOC