我们在阅读 public void doConfigure(Properties properties, LoggerRepository repository) 这个方法的源码的时候,我这里就不讲源码再粘贴出来了,我们
大概了解这个方法分别调用如下几个主要的方法,其他都是一些判断
1,attachListAppender(repository);
2,configureRootCategory(properties, repository);
3,configureLoggerFactory(properties, repository);
4, parseCatsAndRenderers(properties, repository);
5,clearRegistry();
6,detachListAppender(repository);
我们分别到这几个方法的源码中去了解下,他们究竟做了什么:
一,attachListAppender(LoggerRepository repository)
//我们根据之前的章节应该知道,传入的LoggerRepository接口实现类Hierarchy在实现getLogger的方法的时候,判断ht集合中是否存在相同的实例,如果不存在
//会创建以参数为名称的Logger实例,并将这些实例放到ht集合中。所以我们可以知道,这里就是往ht集合里添加了对应的logger对象
Logger ll = repository.getLogger(Constants.LOG4J_PACKAGE_NAME);
//创建一个ListAppender实例,并为他起了个Constants.TEMP_LIST_APPENDER_NAME名字
Appender appender = new ListAppender();
appender.setName(Constants.TEMP_LIST_APPENDER_NAME);
//如何将这个appender添加到logger实例中
ll.addAppender(appender);
ll.setAdditivity(false);
总上述,我们很快就知道,这里就是往ht集合里边添加了含有append实例的logger对象。
二,configureRootCategory(Properties props, LoggerRepository repository)
String effectiveFrefix = ROOT_LOGGER_PREFIX;
//在props对象中获取log4j.rootLogger配置的值
String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props);
if (value == null) {
value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props);
effectiveFrefix = ROOT_CATEGORY_PREFIX;
}
if (value == null) {
getLogger(repository).debug(
"Could not find root logger information. Is this OK?");
} else {
//如果存在,那么我们从Hierachy实例里边获取Logger的实例,如果我们还有记得,这个时候,获取就是我们
//创建Hierachy实例时创建的RootLogger实例
Logger root = repository.getRootLogger();
//对root实行同步锁,然后调用parseCategory方法
synchronized (root) {
parseCategory(
repository, props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value);
}
}
这个方法里边做的事就比较复杂了:
1,根据传递进来的log4j.rootLogger配置的值,通常这个值用逗号“,”分割开。分割开的第一个参数就是调试的等级debug等值,具体有哪些值可以设置,我们可以查看
Level类的常量值
2,然后将这个等级赋值给当前logger对象
3,然后logger对象,将其属性中的AppenderAttachableImpl实例中的Appender进行清除。其实AppenderAttachableImpl实例我们可以看做是封装了Vector的集合
4,然后我们继续迭代log4j.rootLogger配置的值,每迭代一次就获取配置项对应的一个appender实例和Layout实例配置,并将Layout实例放入appender实例中,然后
压入当前Logger对象的AppenderAttachableImpl实例中Vector中。我们举个例子来解析下段代码:
假如我们的配置项如下配置:
log4j.rootLogger=INFO, Console, D, E
那么我们在4进行迭代的时候,
第一次循环
a, 获取appenderName=Console
b,查询配置项里边是否存在log4j.appender.Console配置的值,如果有那么就加载并实例化一个实例,如果没有就实例化一个Append的实例
c,同时将这个实例化的AppenderName设置为Console
d,查询配置项里面是否存在log4j.appender.Console.layout的配置值,如果有那么就进行实例化一个实例,没有就实例化一个Layout的实例
e,将d实例化的layout赋值给b实例化的append属性中
f, 将实例化的append放入到Hashtable实例registry中
g,将封装好的append压入当前Logger对象的AppenderAttachableImpl实例中Vector中
第二次循环
a, 获取appenderName=D
b,查询配置项里边是否存在log4j.appender.D配置的值,如果有那么就加载并实例化一个实例,如果没有就实例化一个Append的实例
c,同时将这个实例化的AppenderName设置为D
d,查询配置项里面是否存在log4j.appender.D.layout的配置值,如果有那么就进行实例化一个实例,没有就实例化一个Layout的实例
e,将d实例化的layout赋值给b实例化的append属性中
f, 将封装好的append压入当前Logger对象的AppenderAttachableImpl实例中Vector中
以此类推。
我们查看下append接口的继承关系:
我们再查看下Layout这个抽象类的继承关系:
从这2者的继承可以知道,我们在配置的时候,对
log4j.appender
log4j.appender.xxx.layout的配置值是蛮多的,究竟他们的功能如何,这里就不深入去了解先了。
三,configureLoggerFactory(Properties props, LoggerRepository repository)
//获取log4j.loggerFactory配置项的值
String factoryClassName =
OptionConverter.findAndSubst(LOGGER_FACTORY_KEY, props);
//如果不为空,那么就实例化
if (factoryClassName != null) {
loggerFactory =
(LoggerFactory) OptionConverter.instantiateByClassName(
factoryClassName, LoggerFactory.class, loggerFactory);
PropertySetter setter = new PropertySetter(loggerFactory);
setter.setLoggerRepository(repository);
setter.setProperties(props, FACTORY_PREFIX + ".");
}
四, parseCatsAndRenderers(Properties props, LoggerRepository repository)
Enumeration enumeration = props.propertyNames();
//1,迭代所有配置项
while (enumeration.hasMoreElements()) {
String key = (String) enumeration.nextElement();
//2,判断是否含有log4j.category.和log4j.logger.配置项开头的
if (key.startsWith(CATEGORY_PREFIX) || key.startsWith(LOGGER_PREFIX)) {
String loggerName = null;
if (key.startsWith(CATEGORY_PREFIX)) {
loggerName = key.substring(CATEGORY_PREFIX.length());
} else if (key.startsWith(LOGGER_PREFIX)) {
loggerName = key.substring(LOGGER_PREFIX.length());
}
//3,根据loggerName产生Loger对象
String value = OptionConverter.findAndSubst(key, props);
Logger logger = repository.getLogger(loggerName, loggerFactory);
//4,如何在调第二步里边的parseCategory逻辑
synchronized (logger) {
parseCategory(repository, props, logger, key, loggerName, value);
parseAdditivityForLogger(repository, props, logger, loggerName);
}
} else if (key.startsWith(RENDERER_PREFIX)) {
//5,如果含有log4j.renderer.前缀的配置项,那么就进行实例或获取,并放入到logRepository对象的属性中
String renderedClass = key.substring(RENDERER_PREFIX.length());
String renderingClass = OptionConverter.findAndSubst(key, props);
if (repository instanceof RendererSupport) {
RendererSupport rs = (RendererSupport) repository;
RendererMap rm = rs.getRendererMap();
rm.addRenderer(renderedClass, renderingClass);
}
}
}
五,clearRegistry()
这里只将在第二步放入registry这个HashTable实例集合中的对象进行清空
六,detachListAppender(LoggerRepository repository)
这里只是将第一步放入到Logger对象中的ListAppender对象进行清空
至此,LogManager静态代码块的代码基本阅读完毕,那么我们就来进行总结下:
1,在静态代码块做了首先是产生Logger,LogRepository和LogRepositorySelector对应的实体,并将这三个实体进行了结合即:
RootLogger实体作为参数传递给Hierarchy的属性中,同时将Hierarchy实例作为DefaultRepositorySelector的属性
2,读取配置文件log4j.properties,并将其转为properties对象
3,读取配置项中的log4j.rootLogger配置值,如何循环产生layout对象赋值给循环产生的append实例属性中,最后将所有的append实例压入Logger对象的属性的Vetor集合中。
4,如何进行了一些清理收尾工作。
5,在这阅读过程中,还有些具体的配置为在进行细致的研读,但是我们可以从这里的层级关系可以知道,其他配置项也必将是按照这种层级关系往下放置的。
LogManager类的静态程序块就完成了,Log4j的加载过程也就完成了,
我们下一回就通过Logger对象的debug方法即logger.debug("Hello world.");来进行调试日志输出源码的也读。
Log4j源码阅读之五—logger.debug("Hello world.");