深入Log4J源码之Log4J Core (四)

2014-11-24 08:26:37 · 作者: · 浏览: 1
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;
17 }
18 return defaultLevel;
19 }
另外,由于对相同级别的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 = "";
8 }
9 }
10 private void writeObject(final ObjectOutputStream output) throws IOException {
11 output.defaultWriteObject();
12 output.writeInt(level);
13 output.writeInt(syslogEquivalent);
14 output.writeUTF(levelStr);
15 }
16 private Object readResolve() throws ObjectStreamException {
17 if(this.getClass() == Level.class) {
18 return toLevel(level);
19 }
20 return this;
21 }

如果要实现自己的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 lineNumber;
2 transient String fileName;
3 transient String className;
4 transient String methodName;
5 //fully.qualified.classname.of.caller.methodName(Filename.java:line)
6 public String fullInfo;
我们知道在异常栈中每一条记录都包含了方法调用对应的这些信息,Log4J的这些信息正是利用了这个原理,即通过构建一个Throwable实例,而后在该Throwable的栈信息中解析出来的:


1 public LocationInfo getLocationInformation() {
2 if (locationInfo == null) {
3 locationInfo = new LocationInfo(new Throwable(),
4 fqnOfCategoryClass);
5 }
6 return locationInfo;
7 }
以上Throwable一般会产生如下异常栈:

1 java.lang.Throwable
2
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.