由于自己工作性質(zhì)發(fā)生了些變化,人隨境遷,回首時,發(fā)現(xiàn)真的是歲月如梭,時光如水。彈指間,從研發(fā)到維護,從總部到出差已經(jīng)是四月有余了。
在維護系統(tǒng)時,難免會時不時地跟代碼再次打些交道。今天想再操刀做個小工具時,居然不太記得了log4j的配置,不得不從apache再重新down了相關(guān)的jar與DOC,匆匆看看了一下,快點寫下來,日后肯定還用得著,同時也希望對網(wǎng)友們有用。
Log4j基本上已經(jīng)是java里的首選日志工具了,它主要由三部分組成:Loggers, Appenders和Layouts (注意后面都加了s啦,顧名思義一個配置中可以分別允許有多個此類對象存在,后面將詳細介紹)。
Loggers-用來定義日志消息的類型及級別;
Appenders-用來定義日志消息的輸出終端;
Layouts-用來定義日志消息的輸出格式。
Logger
Loggers層次:
對logger的名字是大小寫敏感。
規(guī)則-如果類P的名字是另一個類C的名字的前綴,且P與C之間以“.”號連接起來,那么稱P為祖先層次;如果層次A與其子層次之間沒有任何父層次,則認為層次A為父層次。
|
例如,org.apache.log4j是org.apache.log4j.Logger的父層次;而org.apache是org.apache.log4j與org.apache.log4j.Logger的祖先層次。
另外,注意log4j中有個默認的root級別的logger,在所有logger中,它是最高級別,其它所有logger均繼承于root,root有以下二個特性:
1. 它總是存在的。
2. 它不能通過名字直接獲取其實例(root實例可以通過類Logger的靜態(tài)方法Logger.getRootLogger獲得,而其它logger則可以直接通過名字來獲取Logger.getLogger)。
關(guān)于Loggers中的級別:
對每個logger,可以指定其級別,在系統(tǒng)org.apache.log4j.Level中,已經(jīng)定義了五個級別,分別為debug, info, warn, error, fatal。
規(guī)則-設(shè)當(dāng)前logger為X,從X開始往X的父類方向開始算(包括X本身),直到名為root的logger,第一個不為null的級別值就是X的級別值。
|
日志顯示:
級別的定義是為了過濾性地選擇日志。
規(guī)則-若當(dāng)前請方式級別為P,而當(dāng)前的logger的級別為Q,當(dāng)且僅當(dāng)在P>=Q的情況下,日志信息才能顯示。
|
關(guān)于日志中級別的關(guān)系為:DEBUG < INFO < WARN < ERROR < FATAL。
關(guān)于此規(guī)則的說明,有以下代碼為實例:
// 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正如前面所述,是用來定義日志信息的輸出終端,最覺的輸出終端有console與file了,另外還有其它如GUI components, JMS, NT Event Loggers, remote socket servers等等。
Appender也有類似繼承的原則,即當(dāng)前logger的appender包括其它父類的appender。這樣就會出現(xiàn)一個logger可能擁有多個appender了,在現(xiàn)實中看來,就是log4j的日志信息可以同進輸出到console, file等等終端了。當(dāng)然,為了不使此appender惡性疊加,可以通過設(shè)置additivity標(biāo)志來阻止繼承。
規(guī)則-若當(dāng)前logger為C,則C擁有包括其自己及其父類的所有appender。另外,若C的父logger為P,且P的additivity標(biāo)志已經(jīng)設(shè)置成為false,則C只擁有自己及P的appender了,而P則只能擁有本身的appender。
|
以下表格可以清晰說明此規(guī)則:
Logger
Name
|
Added
Appenders
|
Additivity
Flag
|
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.
|
對于layout,也正如前面所述,是用來定義日志信息的輸出格式,它的與C語言中printf函數(shù)的格式定義基本相似,本人對printf中的格式參數(shù)不太熟悉,總感覺有些復(fù)雜,一般是平時看到自己認為有用的格式就記一下,到時直接搬過來。如
log4j.appender.R.layout.ConversionPattern=--->%-d{yyyy-MM-dd HH:mm:ss} [%5p]%l - %m%n
時,日志信息格式為:
--->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文件來完成。
Log4j默認的配置為,通過讀取系統(tǒng)變量log4j.configuration來找到配置文件,當(dāng)然變量的默認值為log4j.properties,所以若沒有設(shè)置此系統(tǒng)變量,可以直接將配置文件命名為log4j.properties,然后放到類路徑下即可。另外,若想引用通過其它配置文件,則可以通過
Loader.getResource(java.lang.String)來讀取指定的配置文件。
其它:
1. 由于有了log4j中的級別繼承機制,所以可以很方便地過濾信息了,不僅可以很方便地限制日志的輸出量,也可以同時將日志輸出到不同的終端。
另外,因為在java文件中,文件的物理層次關(guān)系也是直接通過“.”符號來控制的,且在很在程度上這個物理層次也決定了文件的邏輯層次,所以我們在當(dāng)前文件中獲取logger時,可以直接通過當(dāng)前文件的類名來獲取,如:
static Logger logger = Logger.getLogger(MyApp.class);
這樣,在過濾消息時就很簡單了,簡單示例如下:
在配置文件中有
log4j.rootLogger=ERROR,stdout,R
log4j.category.com.db=DEBUG
log4j.category.com.i18n=INFO
log4j.category.com.zyx=fatal
默認的root logger的日志級別為Error,根據(jù)級別關(guān)系這個級別也相當(dāng)高了,這樣可以減少系統(tǒng)中日志的輸出量,但有些地方可能得輸出更詳細信息,如數(shù)據(jù)庫部分,所以可以將com.db設(shè)置成了debug。另外,我在com.zyx下,我只想看到類型為fatal的日志,也可以如上所設(shè)。
2. log4j.rootLogger=ERROR,stdout, ROLLING_FILE這個定義表示root logger的日志級別為Error,后面的stdout, ROLLING_FILE表示此root有二個appender,通??梢酝ㄟ^這樣來定義日志可以同時向多個終端輸出,因為子logger可以繼承所有父logger的appender.
如我可以通過以下定義將日志同時往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
|
實例
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測試文件:
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");
}
}
|
運行后控制臺輸出:
DEBUG [main](Log4jTest.java:9) - Welcome to by blogblgjava.net/jkallen
INFO [main] (Log4jTest.java:10) - Exitingapplication.
over
|
運行后日志文件輸出:
--->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é)了一下,但也花費了近三個小時,支持下!
參考:log4j DOC