log4j fatal有OFF、FATAL、ERR...

当前位置:&>&&>&&>&
log4j日志级别有哪些?log4j日志级别说明
发布时间:编辑:
有关log4j日志文件级别的说明,log4j日志配置的五种正常级别,还有两个可用的特别的日志记录级别,不了解的朋友参考下。
日志记录器(Logger)是日志处理的核心组件。
log4j具有5种正常级别(Level)。
日志记录器(Logger)的可用级别Level (不包括自定义级别 Level):
public static final Level TRACE
TheTRACELevel designates finer-grained informational events than the DEBUG.Since:1.2.12
static Level DEBUG
DEBUG Level指出细粒度信息事件对调试应用程序是非常有帮助的。
static Level INFO
INFO level表明 消息在粗粒度级别上突出强调应用程序的运行过程。
static Level WARN
WARN level表明会出现潜在错误的情形。
static Level ERROR
ERROR level指出虽然发生错误事件,但仍然不影响系统的继续运行。
static Level FATAL
FATAL level指出每个严重的错误事件将会导致应用程序的退出。
另外,还有两个可用的特别的日志记录级别:
static Level ALL
ALL Level是最低等级的,用于打开所有日志记录。
static Level OFF
OFF Level是最高等级的,用于关闭所有日志记录。
日志记录器(Logger)的行为是分等级的。如下表所示:
分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、ALL或者您定义的级别。
log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。
通过此处定义的级别,可以控制到应用程序中相应级别的日志信息的开关。
比如,定义了info级别,则应用程序中所有debug级别的日志信息将不被打印出来。
程序会打印高于或等于所设置级别的日志,设置的日志等级越高,打印出来的日志就越少。
如果设置级别为info,则优先级高于等于info级别(如:info、warn、error)的日志信息将可以被输出,小于该级别的如debug将不会被输出。
与 log4j日志级别有哪些?log4j日志级别说明 有关的文章
本文标题:
本页链接:
12345678910
12345678910&&&&&&&&&&&&&&&&&&
posts - 362,comments - 0,trackbacks - 0
原文出处:http://www.blogjava.net/DLevin/archive//381667.html。感谢上善若水的无私分享。
在简单的介绍了Log4J各个模块类的作用后,以下将详细的介绍各个模块的具体作用以及代码实现。
Logger是对记录日志动作的抽象,它提供了记录不同级别日志的接口,日志信息可以包含异常信息也可以不包含:
&1&public&void&debug(Object&message)&{
&2&&&&&if(isLevelEnabled(Level.DEBUG))&{
&3&&&&&&&&&forceLog(FQCN,&Level.DEBUG,&message,&null);
&6&public&void&debug(Object&message,&Throwable&cause)&{
&7&&&&&if(isLevelEnabled(Level.DEBUG))&{
&8&&&&&&&&&forceLog(FQCN,&Level.DEBUG,&message,&cause);
11&protected&void&forceLog(String&fqcn,&Level&level,&Object&message,&Throwable&t)&{
12&&&&&callAppenders(new&LoggingEvent(fqcn,&this,&level,&message,&t));
Logger类包含Level信息&,如果当前Logger未设置Level值,它也可以中父节点中继承下来,该值可以用来控制该Logger可以记录的日志级别:
&1&protected&Level&
&2&public&Level&getEffectiveLevel()&{
&3&&&&&for(Logger&logger&=&this;&logger&!=&null;&logger&=&logger.parent)&{
&4&&&&&&&&&if(logger.level&!=&null)&{
&5&&&&&&&&&&&&&return&logger.
&6&&&&&&&&&}
&8&&&&&return&null;
10&public&boolean&isLevelEnabled(Level&level)&{
11&&&&&return&level.isGreaterOrEqual(this.getEffectiveLevel());
13&public&boolean&isDebugEnabled()&{
14&&&&&return&isLevelEnabled(Level.DEBUG);
Logger是一个命名的实体,其名字一般用”.”分割以体现不同Logger的层次关系,其中Level和Appender信息可以从父节点中获取,因而Logger类中还具有name和parent属性。
1&private&String&
2&protected&Logger&
在某些情况下,我们希望某些Logger只将日志记录到特定的Appender中,而不想记录在父节点中的Appender中,Log4J为这种需求提供了additivity属性,即对当前Logger节点,如果其additivity属性设置为false,则该Logger不会继承父节点的Appender信息,但是其子节点依然会继承该Logger的Appender信息,除非子节点的additivity属性也设置成了false。
&1&private&boolean&additive&=&true;
&2&public&void&callAppenders(LoggingEvent&event)&{
&3&&&&&int&writes&=&0;
&5&&&&&for(Logger&logger&=&this;&logger&!=&null;&logger&=&logger.parent)&{
&6&&&&&&&&&synchronized(logger)&{
&7&&&&&&&&&&&&&if(logger.appenders&!=&null)&{
&8&&&&&&&&&&&&&&&&&writes&+=&logger.appenders.appendLoopOnAppenders(event);
&9&&&&&&&&&&&&&}
10&&&&&&&&&&&&&if(!logger.additive)&{
11&&&&&&&&&&&&&&&&&break;
12&&&&&&&&&&&&&}
13&&&&&&&&&}
16&&&&&if(writes&==&0)&{
17&&&&&&&&&System.err.println(&No&Appender&is&configed.&);
最后,为了支持国际化,Log4J还提供了两个l7dlog()方法,通过指定的key,以从资源文件中获取消息内容。为了使用这两个方法,需要设置资源文件。同样,资源文件也是可以从父节点中继承的。
&1&private&ResourceBundle&resourceB
&2&public&void&l7dlog(Level&level,&String&key,&Throwable&cause)&{
&3&&&&&if(isLevelEnabled(level))&{
&4&&&&&&&&&String&message&=&getResourceBundleString(key);
&5&&&&&&&&&if(message&==&null)&{
&6&&&&&&&&&&&&&message&=&
&7&&&&&&&&&}
&8&&&&&&&&&forceLog(FQCN,&level,&message,&cause);
12&public&void&l7dlog(Level&level,&String&key,&Object[]¶ms,&Throwable&cause)&{
14&&&&&&&&&if(pattern&==&null)&{
15&&&&&&&&&&&&&message&=&
16&&&&&&&&&}&else&{
17&&&&&&&&&&&&&message&=&MessageFormat.format(pattern,¶ms);
18&&&&&&&&&}
22&protected&String&getResourceBundleString(String&key)&{
23&&&&&ResourceBundle&rb&=&getResourceBundle();
25&&&&&return&rb.getString(key);
27&public&ResourceBundle&getResourceBundle()&{
28&&&&&for(Logger&logger&=&this;&logger&!=&null;&logger&=&logger.parent)&{
29&&&&&&&&&if(logger.resourceBundle&!=&null)&{
30&&&&&&&&&&&&&return&logger.resourceB
31&&&&&&&&&}
33&&&&&return&null;&&&&
另外,在实际开发中经常会遇到要把日志信息同时写到不同地方,如同时写入文件和控制台,因而一个Logger实例中可以包含多个Appender,为了管理多个Appender,Log4J抽象出了AppenderAttachable接口,它定义了几个用于管理多个Appender实例的方法,这些方法由AppenderAttachableImpl类实现,而Logger会实例化AppenderAttachableImpl,并将这些方法代理给该实例:
&1&public&interface&AppenderAttachable&{
&2&&&&&public&void&addAppender(Appender&newAppender);
&3&&&&&public&Enumeration&getAllAppenders();
&4&&&&&public&Appender&getAppender(String&name);
&5&&&&&public&boolean&isAttached(Appender&appender);
&6&&&&&void&removeAllAppenders();
&7&&&&&void&removeAppender(Appender&appender);
&8&&&&&void&removeAppender(String&name);
RootLogger类
在Log4J中,所有Logger实例组成一个单根的树状结构,由于Logger实例的根节点有一点特殊:它的名字为“root”,它没有父节点,它的Level字段必须设值以防止其他Logger实例都没有设置Level值的情况。基于这些考虑,Log4J通过继承Logger类实现了RootLogger类,它用于表达所有Logger实例的根节点:
&1&public&final&class&RootLogger&extends&Logger&{
&2&&&&&public&RootLogger(Level&level)&{
&3&&&&&&&&&super(&root&);
&4&&&&&&&&&setLevel(level);
&6&&&&&public&final&Level&getChainedLevel()&{
&7&&&&&&&&&return&
&9&&&&&public&final&void&setLevel(Level&level)&{
10&&&&&&&&&if&(level&==&null)&{
11&&&&&&&&&&&&&LogLog.error(&You&have&tried&to&set&a&null&level&to&root.&,
12&&&&&&&&&&&&&&&&&&&&&new&Throwable());
13&&&&&&&&&}&else&{
14&&&&&&&&&&&&&this.level&=&
15&&&&&&&&&}
NOPLogger类
有时候,为了测试等其他需求,我们希望Logger本身不做什么事情,Log4J为这种需求提供了NOPLogger类,它继承自Logger,但是基本上的方法都为空。
Level是对日志级别的抽象,目前Log4J支持的级别有FATAL、ERROR、WARN、INFO、DEBUG、TRACE,从头到尾一次级别递减,另外Log4J还支持两种特殊的级别:ALL和OFF,它们分别表示打开和关闭日志功能。
&1&public&static&final&int&OFF_INT&=&Integer.MAX_VALUE;
&2&public&static&final&int&FATAL_INT&=&50000;
&3&public&static&final&int&ERROR_INT&=&40000;
&4&public&static&final&int&WARN_INT&&=&30000;
&5&public&static&final&int&INFO_INT&&=&20000;
&6&public&static&final&int&DEBUG_INT&=&10000;
&7&public&static&final&int&TRACE_INT&=&5000;
&8&public&static&final&int&ALL_INT&=&Integer.MIN_VALUE;
10&public&static&final&Level&OFF&=&new&Level(OFF_INT,&&OFF&,&0);
11&public&static&final&Level&FATAL&=&new&Level(FATAL_INT,&&FATAL&,&0);
12&public&static&final&Level&ERROR&=&new&Level(ERROR_INT,&&ERROR&,&3);
13&public&static&final&Level&WARN&=&new&Level(WARN_INT,&&WARN&,&4);
14&public&static&final&Level&INFO&=&new&Level(INFO_INT,&&INFO&,&6);
15&public&static&final&Level&DEBUG&=&new&Level(DEBUG_INT,&&DEBUG&,&7);
16&public&static&final&Level&TRACE&=&new&Level(TRACE_INT,&&TRACE&,&7);
17&public&static&final&Level&ALL&=&new&Level(ALL_INT,&&ALL&,&7);
每个Level实例包含了该Level代表的int值(一般是从级别低到级别高一次增大)、该Level的String表达、该Level和系统Level的对应值。
1&protected&transient&int&
2&protected&transient&String&levelS
3&protected&transient&int&syslogE
4&protected&Level(int&level,&String&levelStr,&int&syslogEquivalent)&{
5&&&&&this.level&=&
6&&&&&this.levelStr&=&levelS
7&&&&&this.syslogEquivalent&=&syslogE
Level类主要提供了判断哪个Level级别更高的方法isGreaterOrEqual()以及将int值或String值转换成Level实例的toLevel()方法:
&1&public&boolean&isGreaterOrEqual(Level&level)&{
&2&&&&&return&this.level&&=&level.
&4&public&static&Level&toLevel(int&level)&{
&5&&&&&return&toLevel(level,&DEBUG);
&7&public&static&Level&toLevel(int&level,&Level&defaultLevel)&{
&8&&&&&switch(level)&{
&9&&&&&&&&&case&OFF_INT:&return&OFF;
10&&&&&&&&&case&FATAL_INT:&return&FATAL;
11&&&&&&&&&case&ERROR_INT:&return&ERROR;
12&&&&&&&&&case&WARN_INT:&return&WARN;
13&&&&&&&&&case&INFO_INT:&return&INFO;
14&&&&&&&&&case&DEBUG_INT:&return&DEBUG;
15&&&&&&&&&case&TRACE_INT:&return&TRACE;
16&&&&&&&&&case&ALL_INT:&return&ALL;
18&&&&&return&defaultL
另外,由于对相同级别的Level实例来说,它必须是单例的,因而Log4J对序列化和反序列化做了一些处理。即它的三个成员都是transient,真正序列化和反序列化的代码自己写,并且加入readResolve()方法的支持,以保证反序列化出来的相同级别的Level实例是相同的实例。
&1&private&void&readObject(final&ObjectInputStream&input)&throws&IOException,&ClassNotFoundException&{
&2&&&&&input.defaultReadObject();
&3&&&&&level&=&input.readInt();
&4&&&&&syslogEquivalent&=&input.readInt();
&5&&&&&levelStr&=&input.readUTF();
&6&&&&&if(levelStr&==&null)&{
&7&&&&&&&&&levelStr&=&&&;
10&private&void&writeObject(final&ObjectOutputStream&output)&throws&IOException&{
11&&&&&output.defaultWriteObject();
12&&&&&output.writeInt(level);
13&&&&&output.writeInt(syslogEquivalent);
14&&&&&output.writeUTF(levelStr);
16&private&Object&readResolve()&throws&ObjectStreamException&{
17&&&&&if(this.getClass()&==&Level.class)&{
18&&&&&&&&&return&toLevel(level);
20&&&&&return&this;
如果要实现自己的Level类,可以继承自Level,并且实现相应的静态toLevel()方法即可。关于如何实现自己的Level类将会在配置文件相关小节中详细讨论。
LoggerRepository类
LoggerRepository从概念以及字面上来说它就是一个Logger实例的容器:一方面相同名字的Logger实例只需要创建一次,在后面的使用中,只需要从这个容器中取即可;另一方面,Logger容器可以存放从配置文件中解析出来的信息,从而使配置信息可以无缝的应用到Log4J内部系统中;最后Logger容器还为维护Logger的树状层次结构提供了方面,每个Logger只维护父节点的信息,有了Logger容器的存在则可以很容易的找到一个新的Logger实例的父节点;关于Logger容器将在下一节中详细讲解。
LoggingEvent类
LoggingEvent个人感觉用LoggingContext更合适一些,它是对一次日志记录时哪能获取到的数据的封装。它包含了以下信息以提供Layout在format()方法中使用:
1.fqnOfCategoryClass:日志记录接口(默认为Logger)的类全名,该信息主要用于计算日志记录点的源文件、调用方法以及行号等位置信息。
2.locationInfo:通过fqnOfCategoryClass计算位置信息,位置信息的计算由LocationInfo类实现,这些信息可以提供给Layout使用。
3.logger:目前来看主要是通过Logger实例取得LogRepository实例,并通过LogRepository取得注册的ObjectRender实例,如果有的话。
4.loggerName:当前日志记录的Logger名称,提供给Layout使用。
5.threadName:当前线程名,提供给Layout使用。
6.level:当前日志的级别,提供给Layout使用。
7.message:当前日志类,一般是String类型,但是也可以通过注册ObjectRender,然后传入相应的其他对象类型。
8.renderedMessage:经过ObjectRender处理后的日志信息,提供给Layout使用。
9.throwableInfo:异常信息,如果存在的话,提供给Layout使用。
10.timestamp:创建LoggingEvent实例的时间,提供给Layout使用。
11.其他相对不常用的信息将会在后面小节中讲解。
LoggingEvent只是一个简单的数据对象(DO),因而其实现还是比较简单的,即在创建实例时将数据提供给它,在其他类(Layout等)使用它时通过getXXX()方法取数据。不过还是有几个方法可以简单的讲解一下。
LocationInfo类计算位置信息
LocationInfo所指的位置信息主要包括记录日志所在的源文件名、类名、方法名、所在源文件的行号。
1&&&&&transient&String&lineN
2&&&&&transient&String&fileN
3&&&&&transient&String&classN
4&&&&&transient&String&methodN
5& & &//fully.qualified.classname.of.caller.methodName(Filename.java:line)
6&&&&&public&String&fullI
我们知道在异常栈中每一条记录都包含了方法调用对应的这些信息,Log4J的这些信息正是利用了这个原理,即通过构建一个Throwable实例,而后在该Throwable的栈信息中解析出来的:
1&public&LocationInfo&getLocationInformation()&{
2&&&&&if&(locationInfo&==&null)&{
3&&&&&&&&&locationInfo&=&new&LocationInfo(new&Throwable(),&
4&fqnOfCategoryClass);
6&&&&&return&locationI
以上Throwable一般会产生如下异常栈:
1&java.lang.Throwable
3&at&org.apache.log4j.PatternLayout.format(PatternLayout.java:413)
4&at&org.apache.log4j.FileAppender.doAppend(FileAppender.java:183)
5&at&org.apache.log4j.Category.callAppenders(Category.java:131)
6&at&org.apache.log4j.Category.log(Category.java:512)
7&at&callers.fully.qualified.className.methodName(FileName.java:74)
因而我们就可以通过callers.fully.qualified.className信息来找到改行信息,这个className信息即是传入的fqnOfCategoryClass。
如果当前JDK版本是1.4以上,我们就可以通过JDK提供的一些方法来查找:
&1&getStackTraceMethod&=&Throwable.class.getMethod(&getStackTrace&,
&2&&&&&&&&&noArgs);
&3&Class&stackTraceElementClass&=&Class
&4&&&&&&&&&.forName(&java.lang.StackTraceElement&);
&5&getClassNameMethod&=&stackTraceElementClass.getMethod(
&6&&&&&&&&&&getClassName&,&noArgs);
&7&getMethodNameMethod&=&stackTraceElementClass.getMethod(
&8&&&&&&&&&&getMethodName&,&noArgs);
&9&getFileNameMethod&=&stackTraceElementClass.getMethod(&getFileName&,
10&&&&&&&&&noArgs);
11&getLineNumberMethod&=&stackTraceElementClass.getMethod(
12&&&&&&&&&&getLineNumber&,&noArgs);
14&Object[]&noArgs&=&null;
15&Object[]&elements&=&(Object[])&getStackTraceMethod.invoke(t,
16&&&&&&&&&noArgs);
17&String&prevClass&=&NA;
18&for&(int&i&=&elements.length&-&1;&i&&=&0;&i--)&{
19&&&&&String&thisClass&=&(String)&getClassNameMethod.invoke(
20&&&&&&&&&&&&&elements[i],&noArgs);
21&&&&&if&(fqnOfCallingClass.equals(thisClass))&{
22&&&&&&&&&int&caller&=&i&+&1;
23&&&&&&&&&if&(caller&&&elements.length)&{
24&&&&&&&&&&&&&className&=&prevC
25&&&&&&&&&&&&&methodName&=&(String)&getMethodNameMethod.invoke(
26&&&&&&&&&&&&&&&&&&&&&elements[caller],&noArgs);
27&&&&&&&&&&&&&fileName&=&(String)&getFileNameMethod.invoke(
28&&&&&&&&&&&&&&&&&&&&&elements[caller],&noArgs);
29&&&&&&&&&&&&&if&(fileName&==&null)&{
30&&&&&&&&&&&&&&&&&fileName&=&NA;
31&&&&&&&&&&&&&}
32&&&&&&&&&&&&&int&line&=&((Integer)&getLineNumberMethod.invoke(
33&&&&&&&&&&&&&&&&&&&&&elements[caller],&noArgs)).intValue();
34&&&&&&&&&&&&&if&(line&&&0)&{
35&&&&&&&&&&&&&&&&&lineNumber&=&NA;
36&&&&&&&&&&&&&}&else&{
37&&&&&&&&&&&&&&&&&lineNumber&=&String.valueOf(line);
38&&&&&&&&&&&&&}
39&&&&&&&&&&&&&StringBuffer&buf&=&new&StringBuffer();
40&&&&&&&&&&&&&buf.append(className);
41&&&&&&&&&&&&&buf.append(&.&);
42&&&&&&&&&&&&&buf.append(methodName);
43&&&&&&&&&&&&&buf.append(&(&);
44&&&&&&&&&&&&&buf.append(fileName);
45&&&&&&&&&&&&&buf.append(&:&);
46&&&&&&&&&&&&&buf.append(lineNumber);
47&&&&&&&&&&&&&buf.append(&)&);
48&&&&&&&&&&&&&this.fullInfo&=&buf.toString();
49&&&&&&&&&}
50&&&&&&&&&return;
52&&&&&prevClass&=&thisC
否则,则需要我们通过字符串查找的方式来查找:
&1&String&s;
&2&//&Protect&against&multiple&access&to&sw.
&3&synchronized&(sw)&{
&4&&&&&t.printStackTrace(pw);
&5&&&&&s&=&sw.toString();
&6&&&&&sw.getBuffer().setLength(0);
&8&int&ibegin,&
&9&ibegin&=&s.lastIndexOf(fqnOfCallingClass);
10&if&(ibegin&==&-1)
11&&&&&return;
12&//&See&bug&44888.
13&if&(ibegin&+&fqnOfCallingClass.length()&&&s.length()
14&&&&&&&&&&&&s.charAt(ibegin&+&fqnOfCallingClass.length())&!=&'.')&{
15&&&&&int&i&=&s.lastIndexOf(fqnOfCallingClass&+&&.&);
16&&&&&if&(i&!=&-1)&{
17&&&&&&&&&ibegin&=&i;
21&ibegin&=&s.indexOf(Layout.LINE_SEP,&ibegin);
22&if&(ibegin&==&-1)
23&&&&&return;
24&ibegin&+=&Layout.LINE_SEP_LEN;
26&//&determine&end&of&line
27&iend&=&s.indexOf(Layout.LINE_SEP,&ibegin);
28&if&(iend&==&-1)
29&&&&&return;
31&//&VA&has&a&different&stack&trace&format&which&doesn't
32&//&need&to&skip&the&inital&'at'
33&if&(!inVisualAge)&{
34&&&&&//&back&up&to&first&blank&character
35&&&&&ibegin&=&s.lastIndexOf(&at&&,&iend);
36&&&&&if&(ibegin&==&-1)
37&&&&&&&&&return;
38&&&&&//&Add&3&to&skip&&at&&;
39&&&&&ibegin&+=&3;
41&//&everything&between&is&the&requested&stack&item
42&this.fullInfo&=&s.substring(ibegin,&iend);
对于通过字符串查找到的fullInfo值,在获取其他单个值时还需要做相应的字符串解析:
className:
&1&//&Starting&the&search&from&'('&is&safer&because&there&is
&2&//&potentially&a&dot&between&the&parentheses.
&3&int&iend&=&fullInfo.lastIndexOf('(');
&4&if&(iend&==&-1)
&5&&&&&className&=&NA;
&7&&&&&iend&=&fullInfo.lastIndexOf('.',&iend);
&9&&&&&//&This&is&because&a&stack&trace&in&VisualAge&looks&like:
11&&&&&//&java.lang.RuntimeException
12&&&&&//&java.lang.Throwable()
13&&&&&//&java.lang.Exception()
14&&&&&//&java.lang.RuntimeException()
15&&&&&//&void&test.test.B.print()
16&&&&&//&void&test.test.A.printIndirect()
17&&&&&//&void&test.test.Run.main(java.lang.String&[])
18&&&&&int&ibegin&=&0;
19&&&&&if&(inVisualAge)&{
20&&&&&&&&&ibegin&=&fullInfo.lastIndexOf('&',&iend)&+&1;
23&&&&&if&(iend&==&-1)
24&&&&&&&&&className&=&NA;
25&&&&&else
26&&&&&&&&&className&=&this.fullInfo.substring(ibegin,&iend);
2&int&iend&=&fullInfo.lastIndexOf(':');
3&if&(iend&==&-1)
4&&&&&fileName&=&NA;
6&&&&&int&ibegin&=&fullInfo.lastIndexOf('(',&iend&-&1);
7&&&&&fileName&=&this.fullInfo.substring(ibegin&+&1,&iend);
lineNumber:
1&int&iend&=&fullInfo.lastIndexOf(')');
2&int&ibegin&=&fullInfo.lastIndexOf(':',&iend&-&1);
3&if&(ibegin&==&-1)
4&&&&&lineNumber&=&NA;
6&&&&&lineNumber&=&this.fullInfo.substring(ibegin&+&1,&iend);
methodName:
1&int&iend&=&fullInfo.lastIndexOf('(');
2&int&ibegin&=&fullInfo.lastIndexOf('.',&iend);
3&if&(ibegin&==&-1)
4&&&&&methodName&=&NA;
6&&&&&methodName&=&this.fullInfo.substring(ibegin&+&1,&iend);
ObjectRender接口
Log4J中,对传入的message实例,如果是非String类型,会先使用注册的ObjectRender(在LogRepository中查找注册的ObjectRender信息)处理成String后返回,若没有找到相应的ObjectRender,则使用默认的ObjectRender,它只是调用该消息实例的toString()方法。
&1&public&Object&getMessage()&{
&2&&&&&if&(message&!=&null)&{
&3&&&&&&&&&return&
&4&&&&&}&else&{
&5&&&&&&&&&return&getRenderedMessage();
&8&public&String&getRenderedMessage()&{
&9&&&&&if&(renderedMessage&==&null&&&&message&!=&null)&{
10&&&&&&&&&if&(message&instanceof&String)
11&&&&&&&&&&&&&renderedMessage&=&(String)&
12&&&&&&&&&else&{
13&&&&&&&&&&&&&LoggerRepository&repository&=&logger.getLoggerRepository();
15&&&&&&&&&&&&&if&(repository&instanceof&RendererSupport)&{
16&&&&&&&&&&&&&&&&&RendererSupport&rs&=&(RendererSupport)&
17&&&&&&&&&&&&&&&&&renderedMessage&=&rs.getRendererMap()
18&&&&&&&&&&&&&&&&&&&&&&&&&.findAndRender(message);
19&&&&&&&&&&&&&}&else&{
20&&&&&&&&&&&&&&&&&renderedMessage&=&message.toString();
21&&&&&&&&&&&&&}
22&&&&&&&&&}
24&&&&&return&renderedM
ThrowableInformation类
ThrowableInformation类用以处理异常栈信息,即通过Throwable实例获取异常栈字符串数组。同时还支持自定义的ThrowableRender(在LogRepository中设置),默认的ThrowableRender通过系统printStackTrace()方法来获取信息:
&1&if&(throwable&!=&null)&{
&2&&&&&this.throwableInfo&=&new&ThrowableInformation(throwable,&logger);
&4&ThrowableRenderer&renderer&=&null;
&5&if&(category&!=&null)&{
&6&&&&&LoggerRepository&repo&=&category.getLoggerRepository();
&7&&&&&if&(repo&instanceof&ThrowableRendererSupport)&{
&8&&&&&&&&&renderer&=&((ThrowableRendererSupport)&repo)
&9&&&&&&&&&&&&&&&&&.getThrowableRenderer();
12&if&(renderer&==&null)&{
13&&&&&rep&=&DefaultThrowableRenderer.render(throwable);
14&}&else&{
15&&&&&rep&=&renderer.doRender(throwable);
17&public&static&String[]&render(final&Throwable&throwable)&{
18&&&&&StringWriter&sw&=&new&StringWriter();
19&&&&&PrintWriter&pw&=&new&PrintWriter(sw);
20&&&&&try&{
21&&&&&&&&&throwable.printStackTrace(pw);
22&&&&&}&catch&(RuntimeException&ex)&{
24&&&&&pw.flush();
25&&&&&LineNumberReader&reader&=&new&LineNumberReader(new&StringReader(
26&&&&&&&&&&&&&sw.toString()));
27&&&&&ArrayList&lines&=&new&ArrayList();
28&&&&&try&{
29&&&&&&&&&String&line&=&reader.readLine();
30&&&&&&&&&while&(line&!=&null)&{
31&&&&&&&&&&&&&lines.add(line);
32&&&&&&&&&&&&&line&=&reader.readLine();
33&&&&&&&&&}
34&&&&&}&catch&(IOException&ex)&{
35&&&&&&&&&if&(ex&instanceof&InterruptedIOException)&{
36&&&&&&&&&&&&&Thread.currentThread().interrupt();
37&&&&&&&&&}
38&&&&&&&&&lines.add(ex.toString());
40&&&&&String[]&tempRep&=&new&String[lines.size()];
41&&&&&lines.toArray(tempRep);
42&&&&&return&tempR
Layout负责将LoggingEvent中的信息格式化成一行日志信息。对不同格式的日志可能还需要提供头和尾等信息。另外有些Layout不会处理异常信息,此时ignoresThrowable()方法返回false,并且异常信息需要Appender来处理,如PatternLayout。
&1&public&abstract&class&Layout&implements&OptionHandler&{
&2&&&&&public&final&static&String&LINE_SEP&=&System.getProperty(&line.separator&);
&3&&&&&public&final&static&int&LINE_SEP_LEN&=&LINE_SEP.length();
&4&&&&&abstract&public&String&format(LoggingEvent&event);
&5&&&&&public&String&getContentType()&{
&6&&&&&&&&&return&&text/plain&;
&8&&&&&public&String&getHeader()&{
&9&&&&&&&&&return&null;
11&&&&&public&String&getFooter()&{
12&&&&&&&&&return&null;
14&&&&&abstract&public&boolean&ignoresThrowable();
Layout的实现比较简单,如SimpleLayout对一行日志信息只是打印日志级别信息以及日志信息。
&1&public&class&SimpleLayout&extends&Layout&{
&2&&&&&StringBuffer&sbuf&=&new&StringBuffer(128);
&3&&&&&public&SimpleLayout()&{
&5&&&&&public&void&activateOptions()&{
&7&&&&&public&String&format(LoggingEvent&event)&{
&8&&&&&&&&&sbuf.setLength(0);
&9&&&&&&&&&sbuf.append(event.getLevel().toString());
10&&&&&&&&&sbuf.append(&&-&&);
11&&&&&&&&&sbuf.append(event.getRenderedMessage());
12&&&&&&&&&sbuf.append(LINE_SEP);
13&&&&&&&&&return&sbuf.toString();
15&&&&&public&boolean&ignoresThrowable()&{
16&&&&&&&&&return&true;
关于Layout更详细的信息将会在以后小节中介绍。
Appender接口
Appender负责定义日志输出的目的地,它可以是控制台(ConsoleAppender)、文件(FileAppender)、JMS服务器(JmsLogAppender)、以Email的形式发送出去(SMTPAppender)等。Appender是一个命名的实体,另外它还包含了对Layout、ErrorHandler、Filter等引用:
&1&public&interface&Appender&{
&2&&&&&void&addFilter(Filter&newFilter);
&3&&&&&public&Filter&getFilter();
&4&&&&&public&void&clearFilters();
&5&&&&&public&void&close();
&6&&&&&public&void&doAppend(LoggingEvent&event);
&7&&&&&public&String&getName();
&8&&&&&public&void&setErrorHandler(ErrorHandler&errorHandler);
&9&&&&&public&ErrorHandler&getErrorHandler();
10&&&&&public&void&setLayout(Layout&layout);
11&&&&&public&Layout&getLayout();
12&&&&&public&void&setName(String&name);
13&&&&&public&boolean&requiresLayout();
简单的,在配置文件中,Appender会注册到Logger中,Logger在写日志时,通过继承机制遍历所有注册到它本身和其父节点的Appender(在additivity为true的情况下),调用doAppend()方法,实现日志的写入。在doAppend方法中,若当前Appender注册了Filter,则doAppend还会判断当前日志时候通过了Filter的过滤,通过了Filter的过滤后,如果当前Appender继承自SkeletonAppender,还会检查当前日志级别时候要比当前Appender本身的日志级别阀门要打,所有这些都通过后,才会将LoggingEvent实例传递给Layout实例以格式化成一行日志信息,最后写入相应的目的地,在这些操作中,任何出现的错误都由ErrorHandler字段来处理。
SkeletonAppender类
目前Log4J实现的Appender都继承自SkeletonAppender类,该类对Appender接口提供了最基本的实现,并且引入了Threshold的概念,即所有的比当前Appender定义的日志级别阀指要大的日志才会记录下来。
&1&public&abstract&class&AppenderSkeleton&implements&Appender,&OptionHandler&{
&2&&&&&protected&Layout&
&3&&&&&protected&String&
&4&&&&&protected&Priority&
&5&&&&&protected&ErrorHandler&errorHandler&=&new&OnlyOnceErrorHandler();
&6&&&&&protected&Filter&headF
&7&&&&&protected&Filter&tailF
&8&&&&&protected&boolean&closed&=&false;
&9&&&&&public&AppenderSkeleton()&{
10&&&&&&&&&super();
12&&&&&public&void&activateOptions()&{
14&&&&&abstract&protected&void&append(LoggingEvent&event);
15&&&&&public&boolean&isAsSevereAsThreshold(Priority&priority)&{
16&&&&&&&&&return&((threshold&==&null)&||&priority.isGreaterOrEqual(threshold));
18&&&&&public&synchronized&void&doAppend(LoggingEvent&event)&{
19&&&&&&&&&if&(closed)&{
20&&&&&&&&&&&&&LogLog.error(&Attempted&to&append&to&closed&appender&named&[&
21&&&&&&&&&&&&&&&&&&&&&+&name&+&&].&);
22&&&&&&&&&&&&&return;
23&&&&&&&&&}
24&&&&&&&&&if&(!isAsSevereAsThreshold(event.getLevel()))&{
25&&&&&&&&&&&&&return;
26&&&&&&&&&}
27&&&&&&&&&Filter&f&=&this.headF
28&&&&&&&&&FILTER_LOOP:&while&(f&!=&null)&{
29&&&&&&&&&&&&&switch&(f.decide(event))&{
30&&&&&&&&&&&&&case&Filter.DENY:
31&&&&&&&&&&&&&&&&&return;
32&&&&&&&&&&&&&case&Filter.ACCEPT:
33&&&&&&&&&&&&&&&&&break&FILTER_LOOP;
34&&&&&&&&&&&&&case&Filter.NEUTRAL:
35&&&&&&&&&&&&&&&&&f&=&f.getNext();
36&&&&&&&&&&&&&}
37&&&&&&&&&}
38&&&&&&&&&this.append(event);
40&public&void&finalize()&{
41&&&&&&&&&if&(this.closed)
42&&&&&&&&&&&&&return;
43&&&&&&&&&LogLog.debug(&Finalizing&appender&named&[&&+&name&+&&].&);
44&&&&&&&&&close();
SkeletonAppender实现了doAppend()方法,它首先检查日志级别是否要比threshold要大;然后如果注册了Filter,则使用Filter对LoggingEvent实例进行过滤,如果Filter返回Filter.DENY则doAppend()退出,否则执行append()方法,该方法由子类实现。
在Log4J中,Filter组成一条链,它定了以decide()方法,由子类实现,若返回DENY则日志不会被记录、NEUTRAL则继续检查下一个Filter实例、ACCEPT则Filter通过,继续执行后面的写日志操作。使用Filter可以为Appender加入一些出了threshold以外的其他逻辑,由于它本身是链状的,而且它的执行是横跨在Appender的doAppend方法中,因而这也是一个典型的AOP的概念。Filter的实现将会在下一小节中讲解。
SkeletonAppender还重写了finalize()方法,这是因为Log4J本身作为一个组件,它可能还是通过其他组件如commons-logging或slf4j组件间接的引入,因而使用它的程序不应该对它存在依赖的,然而在程序退出之前所有的Appender需要调用close()方法以释放它所占据的资源,为了不在使用Log4J的程序手动的close()的方法,以减少Log4J代码的侵入性,因而Log4J将close()的方法调用加入到finalize()方法中,即在垃圾回收器回收Appender实例时就会调用它的close()方法。
WriterAppender类和ConsoleAppender类
WriterAppender将日志写入Java IO中,它继承自SkeletonAppender类。它引入了三个字段:immediateFlush,指定没写完一条日志后,即将日志内容刷新到设备中,虽然这么做会有一点性能上的损失,但是如果不怎么做,则会出现在程序异常终止的时候无法看到部分日志信息,而经常这些丢失的日志信息要用于分析为什么会出现异常终止的情况,因而一般推荐将该值设置为true,即默认值;econding用于定义日志文本的编码方式;qw定义写日志的writer,它可以是文件或是控制台等Java
IO支持的流。
在写日志文本前,WriterAppender还会做其他检查,如该Appender不能已经closed、qw和layout必须有值等,而后才可以将layout格式化后的日志行写入设备中。若layout本身不处理异常问题,则有Appender处理异常问题。最后如果每行日志需要刷新,则调用刷新操作。
&1&public&class&WriterAppender&extends&AppenderSkeleton&{
&2&&&&&protected&boolean&immediateFlush&=&true;
&3&&&&&protected&String&
&4&&&&&protected&QuietWriter&
&5&&&&&public&WriterAppender()&{
&7&&&&&public&WriterAppender(Layout&layout,&OutputStream&os)&{
&8&&&&&&&&&this(layout,&new&OutputStreamWriter(os));
10&&&&&public&WriterAppender(Layout&layout,&Writer&writer)&{
11&&&&&&&&&this.layout&=&
12&&&&&&&&&this.setWriter(writer);
14&&&&&public&void&append(LoggingEvent&event)&{
15&&&&&&&&&if&(!checkEntryConditions())&{
16&&&&&&&&&&&&&return;
17&&&&&&&&&}
18&&&&&&&&&subAppend(event);
20&&&&&protected&boolean&checkEntryConditions()&{
21&&&&&&&&&if&(this.closed)&{
22&&&&&&&&&&&&&LogLog.warn(&Not&allowed&to&write&to&a&closed&appender.&);
23&&&&&&&&&&&&&return&false;
24&&&&&&&&&}
25&&&&&&&&&if&(this.qw&==&null)&{
26&&&&&&&&&&&&&errorHandler
27&&&&&&&&&&&&&&&&&&&&&.error(&No&output&stream&or&file&set&for&the&appender&named&[&
28&&&&&&&&&&&&&&&&&&&&&&&&&&&&&+&name&+&&].&);
29&&&&&&&&&&&&&return&false;
30&&&&&&&&&}
31&&&&&&&&&if&(this.layout&==&null)&{
32&&&&&&&&&&&&&errorHandler.error(&No&layout&set&for&the&appender&named&[&&+&name
33&&&&&&&&&&&&&&&&&&&&&+&&].&);
34&&&&&&&&&&&&&return&false;
35&&&&&&&&&}
36&&&&&&&&&return&true;
38&&&&&protected&void&subAppend(LoggingEvent&event)&{
39&&&&&&&&&this.qw.write(this.layout.format(event));
40&&&&&&&&&if&(layout.ignoresThrowable())&{
41&&&&&&&&&&&&&String[]&s&=&event.getThrowableStrRep();
42&&&&&&&&&&&&&if&(s&!=&null)&{
43&&&&&&&&&&&&&&&&&int&len&=&s.
44&&&&&&&&&&&&&&&&&for&(int&i&=&0;&i&&&&i++)&{
45&&&&&&&&&&&&&&&&&&&&&this.qw.write(s[i]);
46&&&&&&&&&&&&&&&&&&&&&this.qw.write(Layout.LINE_SEP);
47&&&&&&&&&&&&&&&&&}
48&&&&&&&&&&&&&}
49&&&&&&&&&}
50&&&&&&&&&if&(shouldFlush(event))&{
51&&&&&&&&&&&&&this.qw.flush();
52&&&&&&&&&}
54&&&&&public&boolean&requiresLayout()&{
55&&&&&&&&&return&true;
ConsoleAppender继承自WriterAppender,它只是简单的将System.out或System.err实例传递给WriterAppender以构建相应的writer,最后实现将日志写入到控制台中。
在Log4J中,Filter组成一条链,它定了以decide()方法,由子类实现,若返回DENY则日志不会被记录、NEUTRAL则继续检查下一个Filter实例、ACCEPT则Filter通过,继续执行后面的写日志操作。使用Filter可以为Appender加入一些出了threshold以外的其他逻辑,由于它本身是链状的,而且它的执行是横跨在Appender的doAppend方法中,因而这也是一个典型的AOP的概念。
&1&public&abstract&class&Filter&implements&OptionHandler&{
&2&&&&&public&Filter&
&3&&&&&public&static&final&int&DENY&=&-1;
&4&&&&&public&static&final&int&NEUTRAL&=&0;
&5&&&&&public&static&final&int&ACCEPT&=&1;
&6&&&&&public&void&activateOptions()&{
&8&&&&&abstract&public&int&decide(LoggingEvent&event);
&9&&&&&public&void&setNext(Filter&next)&{
10&&&&&&&&&this.next&=&
12&&&&&public&Filter&getNext()&{
13&&&&&&&&&return&
Log4J本身提供了四个Filter:DenyAllFilter、LevelMatchFilter、LevelRangeFilter、StringMatchFilter。
DenyAllFilter只是简单的在decide()中返回DENY值,可以将其应用在Filter链尾,实现如果之前的Filter都没有通过,则该LoggingEvent没有通过,类似或的操作:
1&public&class&DenyAllFilter&extends&Filter&{
2&&&&&public&int&decide(LoggingEvent&event)&{
3&&&&&&&&&return&Filter.DENY;
StringMatchFilter通过日志消息中的字符串来判断Filter后的状态:
&1&public&class&StringMatchFilter&extends&Filter&{
&2&&&&&boolean&acceptOnMatch&=&true;
&3&&&&&String&stringToM
&4&&&&&public&int&decide(LoggingEvent&event)&{
&5&&&&&&&&&String&msg&=&event.getRenderedMessage();
&6&&&&&&&&&if&(msg&==&null&||&stringToMatch&==&null)
&7&&&&&&&&&&&&&return&Filter.NEUTRAL;
&8&&&&&&&&&if&(msg.indexOf(stringToMatch)&==&-1)&{
&9&&&&&&&&&&&&&return&Filter.NEUTRAL;
10&&&&&&&&&}&else&{&//&we've&got&a&match
11&&&&&&&&&&&&&if&(acceptOnMatch)&{
12&&&&&&&&&&&&&&&&&return&Filter.ACCEPT;
13&&&&&&&&&&&&&}&else&{
14&&&&&&&&&&&&&&&&&return&Filter.DENY;
15&&&&&&&&&&&&&}
16&&&&&&&&&}
LevelMatchFilter判断日志级别是否和设置的级别匹配以决定Filter后的状态:
&1&public&class&LevelMatchFilter&extends&Filter&{
&2&&&&&boolean&acceptOnMatch&=&true;&&&&
&3&Level&levelToM
&4&&&&&public&int&decide(LoggingEvent&event)&{
&5&&&&&&&&&if&(this.levelToMatch&==&null)&{
&6&&&&&&&&&&&&&return&Filter.NEUTRAL;
&7&&&&&&&&&}
&8&&&&&&&&&boolean&matchOccured&=&false;
&9&&&&&&&&&if&(this.levelToMatch.equals(event.getLevel()))&{
10&&&&&&&&&&&&&matchOccured&=&true;
11&&&&&&&&&}
12&&&&&&&&&if&(matchOccured)&{
13&&&&&&&&&&&&&if&(this.acceptOnMatch)
14&&&&&&&&&&&&&&&&&return&Filter.ACCEPT;
15&&&&&&&&&&&&&else
16&&&&&&&&&&&&&&&&&return&Filter.DENY;
17&&&&&&&&&}&else&{
18&&&&&&&&&&&&&return&Filter.NEUTRAL;
19&&&&&&&&&}
LevelRangeFilter判断日志级别是否在设置的级别范围内以决定Filter后的状态:
&1&public&class&LevelRangeFilter&extends&Filter&{
&2&&&&&boolean&acceptOnMatch&=&false;
&3&&&&&Level&levelM
&4&&&&&Level&levelM
&5&&&&&public&int&decide(LoggingEvent&event)&{
&6&&&&&&&&&if&(this.levelMin&!=&null)&{
&7&&&&&&&&&&&&&if&(event.getLevel().isGreaterOrEqual(levelMin)&==&false)&{
&8&&&&&&&&&&&&&&&&&return&Filter.DENY;
&9&&&&&&&&&&&&&}
10&&&&&&&&&}
11&&&&&&&&&if&(this.levelMax&!=&null)&{
12&&&&&&&&&&&&&if&(event.getLevel().toInt()&&&levelMax.toInt())&{
13&&&&&&&&&&&&&&&&&return&Filter.DENY;
14&&&&&&&&&&&&&}
15&&&&&&&&&}
16&&&&&&&&&if&(acceptOnMatch)&{
17&&&&&&&&&&&&&return&Filter.ACCEPT;
18&&&&&&&&&}&else&{
19&&&&&&&&&&&&&return&Filter.NEUTRAL;
20&&&&&&&&&}
这一系列终于是结束了。本文主要介绍了Log4J核心类的实现和他们之间的交互关系。涉及到各个模块本身的其他详细信息将会在接下来的小节中详细介绍,如LogRepository与配置信息、Appender类结构的详细信息、Layout类结构的详细信息以及部分LoggingEvent提供的高级功能。而像Level、Logger本身,由于内容不多,已经在这一小节中全部介绍完了。
阅读(...) 评论()

参考资料

 

随机推荐