由于自己工作性质发生了些变化,人随境迁,回首时,发现真的是岁月如梭,时光如水。弹指间,从研发到维护,从总部到出差已经是四月有余了。
在维护系统时,难免会时不时地跟代码再次打些交道。今天想再操刀做个小工具时,居然不太记得了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
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
函数的格式定义基本相似,本人对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.
好啦,现在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
|
马马虎虎总结了一下,但也花费了二个多小时,呵呵!