尽管slf4j+logback看起来很不错。但是,由于自己熟悉的很多项目都还是用log4j。而且springmvc默认使用log4j。所以 自己还是学习总结下。
我习惯到官网去看资料学习使用。我觉得这样不会让我在众多转来转去文章中。搞得晕头转向,而不得要领。
log4j apache官网手册页面。
手册页上有一段话,吸引了我的注意。本来下午的时候,我还在想理由解释为什么要使用日志。下面这段话太经典了。
As Brian W. Kernighan and Rob Pike put it in their truly excellent book "The Practice of Programming"
/**
As personal choice, we tend not to use debuggers beyond getting a
stack trace or the value of a variable or two. One reason is that it
is easy to get lost in details of complicated data structures and
control flow; we find stepping through a program less productive
than thinking harder and adding output statements and self-checking
code at critical places. Clicking over statements takes longer than
scanning the output of judiciously-placed displays. It takes less
time to decide where to put print statements than to single-step to
the critical section of code, even assuming we know where that
is. More important, debugging statements stay with the program;
debugging sessions are transient.
**/
Log4j有三大主要组件:loggers,appends,layouts
这三种类型的组件协同工作使得开发者可以根据消息类型和级别来log信息,并可以在运行时指定信息如何格式化及输出到哪里。
一: Logger 的层次结构
Logger是具名实体,Logger名大小写敏感。遵循下面的规则。
如一个名为"com.foo.Bar"的Logger是一个名为"com.foo"的Logger的子Logger。
即"com.foo"Logger是"com.foo.Bar"的父Logger。
root Logger处于Logger层次结构的顶层。它有两个特殊的地方。
1. root Logger永远存在。
2. root Logger不能通过名字访问到。
通过类静态方法Logger.getRootLogger()可以访问root Logger。
所以其它的Logger实例可以通过类静态方法Logger.getLogger(String name)有过Logger名作为参数来获得。
//Logger类的outline如下
org.apache.log4j.Logger
org.apache.log4j.Logger.FQCN
org.apache.log4j.Logger.Logger(String)
org.apache.log4j.Logger.getLogger(String)
org.apache.log4j.Logger.getLogger(Class)
org.apache.log4j.Logger.getRootLogger()
org.apache.log4j.Logger.getLogger(String, LoggerFactory)
org.apache.log4j.Logger.trace(Object)
org.apache.log4j.Logger.trace(Object, Throwable)
org.apache.log4j.Logger.isTraceEnabled()
Logger的级别。
以下是Logger级别 Level类的outline
org.apache.log4j.Level
org.apache.log4j.Level.TRACE_INT
org.apache.log4j.Level.OFF
org.apache.log4j.Level.FATAL
org.apache.log4j.Level.ERROR
org.apache.log4j.Level.WARN
org.apache.log4j.Level.INFO
org.apache.log4j.Level.DEBUG
org.apache.log4j.Level.TRACE
org.apache.log4j.Level.ALL
org.apache.log4j.Level.serialVersionUID
org.apache.log4j.Level.Level(int, String, int)
org.apache.log4j.Level.toLevel(String)
org.apache.log4j.Level.toLevel(int)
org.apache.log4j.Level.toLevel(int, Level)
org.apache.log4j.Level.toLevel(String, Level)
org.apache.log4j.Level.readObject(ObjectInputStream)
org.apache.log4j.Level.writeObject(ObjectOutputStream)
org.apache.log4j.Level.readResolve()
上面outline的声明中,的顺序也是控制log输出的顺序。
Logger级别的继承:
一个名为C的Logger它的级别等于第一个在继承层级中非空的层级。
Logger的层次结构是一颗倒挂的树。Logger如果没有声明一个层级的话,会向上递归向上查找父Logger的层次并继承。
注意:以同一个名字调用getLogger(String name)方法会返回同一个logger对象。
二: Appenders
在log4j中一个Appender就是一个log信息输出目的地。
一个Logger可以对应多个Appender。
addAppendar()方法可以为Logger添加一个指定的Appender。
任何一个被接受的Logging请求都会被发送到Logger所有的Appendar中,以及继承自此层级的Appender中。(也就是说跟Logger的层级级别可以继承一样。Appender也可以)
注意可以通过设置一个Logger的Appender的additivity flag为false,来阻止logging请求消息向子appender传播。
Appenders接口的outline如下:
org.apache.log4j.Appender
org.apache.log4j.Appender.addFilter(Filter)
org.apache.log4j.Appender.getFilter()
org.apache.log4j.Appender.clearFilters()
org.apache.log4j.Appender.close()
org.apache.log4j.Appender.doAppend(LoggingEvent)
org.apache.log4j.Appender.getName()
org.apache.log4j.Appender.setErrorHandler(ErrorHandler)
org.apache.log4j.Appender.getErrorHandler()
org.apache.log4j.Appender.setLayout(Layout)
org.apache.log4j.Appender.getLayout()
org.apache.log4j.Appender.setName(String)
org.apache.log4j.Appender.requiresLayout()
三:Layouts
Appender来设置log消息输出的目的地。而Layouts则是用来设置输出的log消息的格式的。
可以通过为一个Appender关联一个Layout来完成这个目的。
PatternLayout是log4j的一个标准输出组件。它允许用户以类似于C语言printf风格的模式来指定输出样式。在log4j附加部分中有一个EnhancedPatternLayout。应该优先使用。
log4j的输出模式如下:
%[modifiers]conversion_char
即以一个百分号开头,带一个可选的修饰符和一个转换字符。
假设模式是“%-5p [%t]: %m%n"并且log4j的Layout选择是PatternLayout。
那么下面的代码
Category root = Category.getRoot();
root.debug("Message 1");
root.warn("Message 2");
将产生如下输出:
/**
DEBUG [main]: Message 1
WARN [main]: Message 2
**/
%-5p表示logging 事件的优先级应当以5个字符的宽度左对齐
关于配置的更多信息可以参与如下文章,此文章下面例出的其它文章也可以参考:
log4j详解与实战
根据org.apache.log4j.PatternLayout类的JavaDoc。
我简单概括下各种模式字符的用法:
%c{n} 输出loggineEvent的类别名。c后面跟以花括号括住的精确符例如类别名"a.b.c"模式名%c{2}将会输出"b.c"
%C{n} 同上作用于类别,警告:此模式字符会影响性能。
%d{SimpleDateFormat} 用于输出日志产生时间。日期格式化模式用java中的SimpleDateFormat类。
%F 输出首先log请求的文件名。警告:会影响性能。
%I 输出产生log的调用位置。位置log取决于JVM实现,但通包括调用方法的全称。注意:这种log很有用。但是会更大的影响性能。
%L 输出产生log的代码行。 警告:严重影响性能
%m 用于输出程序指明的日志信息。 最基本的。
%M 输出产生log请求的方法。警告:会影响性能。
%n 换行
%p 输出logging 事件。如debug,info,warn,error
%r 输出构造log使用的时间
%t 输出产生log请求的线程名。
%x 用于输出与产生log线程关联的NDC。
%X 用于输出MDC
%% 输出百分号。
设置log消息输出到文件中也有几个log4j自带的Appender可用,
如:org.apache.log4j.DailyRollingFileAppender
基本设置如下:
<!-- 日志文件输出设置 -->
<appender name="file" class="org.apache.log4j.DailyRollingFileAppender">
<param name="file" value="logs/app_log.log"/>
<param name="DatePattern" value=".yyyy-MM-dd"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%-5p] %d{yyyy-MM-dd HH:mm:ss} %c{3} - %m%n" />
</layout>
</appender>
值得注意的一个问题就是:
在我的spring框架下的web环境下。
日志文件输出的地方。在我的Ubuntu系统下。
它输出的位置地在主目录下的logs目录下。
在windows系统中应该是指在我的文档处吧。
而在一般的java程序配置下时,它的输出在
项目根目录下。
关于过滤日志级别的配置可以参考以下iteye帖子:
log4j不同级别日志分不同文件记录
一个错误:
Caused by: java.io.FileNotFoundException: /home/banxi1988/work/posterWallworkspace/LogDemo/src/log4j.dtd (No such file or directory)
原因是在log4j.xml所在目录下多出来一个log4j.out.xml文件。
我奇怪的是为什么的我的日志文件加日期名配置就是不成功:
下面的代码我也尝试了还几种写法。
<param name="DatePattern" value=".yyyy-MM-dd"/>
<!-- 下面的也尝试过。-->
<param name="DatePattern" value="'.'yyyy-MM-dd"/>
再尝试换一个属性,报如下信息。看来我是暂时没有办法了。
log4j:WARN No such property [fileNamePattern] in org.apache.log4j.DailyRollingFileAppender.
使用学习暂时到此,未完待续-----------