由于自己工作性质发生了些变化,人随境迁,回首时,发现真的是岁月如梭,时光如水。弹指间,从研发到维护,从总部到出差已经是四月有余了。
在维护系统时,难免会时不时地跟代码再次打些交道。今天想再操刀做个小工具时,居然不太记得了log4j的配置,不得不从apache再重新down了相关的jar与DOC,匆匆看看了一下,快点写下来,日后肯定还用得着,同时也希望对网友们有用。
Log4j基本上已经是java里的首选日志工具了,它主要由三部分组成:Loggers, Appenders和Layouts (注意后面都加了s啦,顾名思义一个配置中可以分别允许有多个此类对象存在,后面将详细介绍)。
Loggers-用来定义日志消息的类型及级别;
Appenders-用来定义日志消息的输出终端;
Layouts-用来定义日志消息的输出格式。
Logger
Loggers层次:
对logger的名字是大小写敏感。
规则-如果类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的静态方法Logger.getRootLogger获得,而其它logger则可以直接通过名字来获取Logger.getLogger)。
关于Loggers中的级别:
对每个logger,可以指定其级别,在系统org.apache.log4j.Level中,已经定义了五个级别,分别为debug, info, warn, error, fatal。
规则-设当前logger为X,从X开始往X的父类方向开始算(包括X本身),直到名为root的logger,第一个不为null的级别值就是X的级别值。 |
日志显示:
级别的定义是为了过滤性地选择日志。
规则-若当前请方式级别为P,而当前的logger的级别为Q,当且仅当在P>=Q的情况下,日志信息才能显示。 |
关于日志中级别的关系为:DEBUG < INFO < WARN < ERROR < FATAL。
关于此规则的说明,有以下代码为实例:
// 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也有类似继承的原则,即当前logger的appender包括其它父类的appender。这样就会出现一个logger可能拥有多个appender了,在现实中看来,就是log4j的日志信息可以同进输出到console, file等等终端了。当然,为了不使此appender恶性叠加,可以通过设置additivity标志来阻止继承。
规则-若当前logger为C,则C拥有包括其自己及其父类的所有appender。另外,若C的父logger为P,且P的additivity标志已经设置成为false,则C只拥有自己及P的appender了,而P则只能拥有本身的appender。 |
以下表格可以清晰说明此规则:
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. |
对于layout,也正如前面所述,是用来定义日志信息的输出格式,它的与C语言中printf函数的格式定义基本相似,本人对printf中的格式参数不太熟悉,总感觉有些复杂,一般是平时看到自己认为有用的格式就记一下,到时直接搬过来。如
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 好啦,现在log4j的理论很肤浅地扯了一下,现在可以开始配置了。
配置:
Log4j的配置可以通过加载Java的properties配置文件或者XML文件来完成。
Log4j默认的配置为,通过读取系统变量log4j.configuration来找到配置文件,当然变量的默认值为log4j.properties,所以若没有设置此系统变量,可以直接将配置文件命名为log4j.properties,然后放到类路径下即可。另外,若想引用通过其它配置文件,则可以通过
Loader.getResource(java.lang.String)来读取指定的配置文件。
其它:
1. 由于有了log4j中的级别继承机制,所以可以很方便地过滤信息了,不仅可以很方便地限制日志的输出量,也可以同时将日志输出到不同的终端。
另外,因为在java文件中,文件的物理层次关系也是直接通过“.”符号来控制的,且在很在程度上这个物理层次也决定了文件的逻辑层次,所以我们在当前文件中获取logger时,可以直接通过当前文件的类名来获取,如:
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,根据级别关系这个级别也相当高了,这样可以减少系统中日志的输出量,但有些地方可能得输出更详细信息,如数据库部分,所以可以将com.db设置成了debug。另外,我在com.zyx下,我只想看到类型为fatal的日志,也可以如上所设。
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. |
马马虎虎总结了一下,但也花费了近三个小时,支持下!
参考:log4j DOC