Log4j源码解析
主要流程
Logger logger = Logger.getLogger(Main.class);
1、通过Logger.getLogger(Class clazz) 或 Logger.getLogger(String name)进入。
2、加载LogManager进jvm, 执行静态代码块执行初始化, 创建出RepositorySelector实例及LoggerRepository实例(Hierarchy)。
3、调用OptionConverter.selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy)方法执行配置. (传递Hierarchy)。
4、在OptionConverter中根据存在的配置文件类型创建Configurator实例(这里假设配置文件为log4j.properties), 执行Configurator的doConfigure(URL url, LoggerRepository repository) (传递Hierarchy)。
5、在PropertyConfigurator完成所有组件配置。
6、configureRootCategory(Properties props, LoggerRepository hierarchy) 配置根logger及其appdender等属性。
7、parseCatsAndRenderers(Properties props, LoggerRepository hierarchy) 配置非根的logger及其appdender等属性, 渲染器等。
8、LogManager完成静态代码块后, 通过Hierarchy.getLogger(String name)方法获取出logger对象。
logger.info("xxx");
1、从main方法出发, 这里示例是调用info()方法. (故后续都是判断info级别)。
2、判断是否满足全局的level级别>=info, 满足则 再判断本logger的level级别是否>=info(本logger没有值则找其父类直至有值)。
3、全局level或本体系的level有其一不满足>=info, 提前结束流程。
4、执行本logger再递归父类logger, 执行其aai.appendLoopOnAppenders()方法 (注: 当前logger执行完会判断additive属性, 为false则不再递归父类, 该点在流程图第20步体现)。
5、遍历当前logger的日志输出器appender, 执行其doAppend()方法
6、进入appender的公共父类AppenderSkeleton的doAppend()方法, 先判断当前appender的日志级别level是否>=info。
7、当前appender日志级别level<info, 结束该appender的执行, 遍历下一个appender。
8、递归执行本appender的过滤器执行链。
9、Filter中进行过滤判定。
10、不满足过滤条件则结束该appender的执行, 遍历下一个appender。
11、执行当前appender的append()方法. (这里假设实现类为RollingFileAppender)。
12、进入appender的公共父类WriterAppender的append()方法, 执行子类RollingFileAppender的subAppend()方法。
13、进入RollingFileAppender的subAppend()方法, 立即调用父类WriterAppender的subAppend()方法, 执行后再执行本类的滚动文件逻辑(该点在流程图第19步体现)。
14、在WriterAppender的subAppend()方法中调用layout执行内容格式化。
15、layout完成输出内容格式化, 返回内容信息。
16、调用QuietWriter将内容写入缓存。
17、在WriterAppender中, 判断属性是否为立即输出, 是则调用QuietWriter写出内容。
18、QuietWriter将内容写出到文件中(若appender为ConsoleAppender则是输出到控制台)。
19、回到RollingFileAppender的subAppend()方法中, 判断是否满足滚动文件逻辑, 是则执行rollOver()。
20、遍历完当前logger后, 判断当前logger的additive属性, 为false则不再递归父类, 提前结束流程。
例:
public class Main {
public static void main(String[] args) {
Logger logger = Logger.getLogger(Main.class);
logger.debug("我爱你中国");
}
}
一、 Logger logger = Logger.getLogger(Main.class);
1.1、跟进到LogManager的static代码块。
static {
Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG));
repositorySelector = new DefaultRepositorySelector(h);
String override = OptionConverter.getSystemProperty("log4j.defaultInitOverride", (String)null);
if (override != null && !"false".equalsIgnoreCase(override)) {
LogLog.debug("Default initialization of overridden by log4j.defaultInitOverrideproperty.");
} else {
String configurationOptionStr = OptionConverter.getSystemProperty("log4j.configuration", (String)null);
String configuratorClassName = OptionConverter.getSystemProperty("log4j.configuratorClass", (String)null);
URL url = null;
if (configurationOptionStr == null) {
url = Loader.getResource("log4j.xml");
if (url == null) {
url = Loader.getResource("log4j.properties");
}
} else {
try {
url = new URL(configurationOptionStr);
} catch (MalformedURLException var7) {
url = Loader.getResource(configurationOptionStr);
}
}
if (url != null) {
LogLog.debug("Using URL [" + url + "] for automatic log4j configuration.");
try {
OptionConverter.selectAndConfigure(url, configuratorClassName, getLoggerRepository());
} catch (NoClassDefFoundError var6) {
LogLog.warn("Error during default initialization", var6);
}
} else {
LogLog.debug("Could not find resource: [" + configurationOptionStr + "].");
}
}
}
1.2、
1.2.1、初始化repositorySelector, 并设置选择器的LoggerRepository为Hierarchy, 创建根logger。
1.2.2、先查找log4j.xml, 没有再查找log4j.properties文件。
1.2.3、读取配置, 完成logger初始化 OptionConverter.selectAndConfigure(...);
1.3、跟进OptionConverter.selectAndConfigure(url, configuratorClassName, getLoggerRepository());
public static void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy) {
Configurator configurator = null;
String filename = url.getFile();
if (clazz == null && filename != null && filename.endsWith(".xml")) {
clazz = "org.apache.log4j.xml.DOMConfigurator";
}
if (clazz != null) {
LogLog.debug("Preferred configurator class: " + clazz);
configurator = (Configurator)instantiateByClassName(clazz, Configurator.class, (Object)null);
if (configurator == null) {
LogLog.error("Could not instantiate configurator [" + clazz + "].");
return;
}
} else {
configurator = new PropertyConfigurator();
}
((Configurator)configurator).doConfigure(url, hierarchy);
}
1.3.1、配置文件为log4j.xml, 则使用 DOMConfigurator来解析。
1.3.2、配置文件为log4j.properties, 则使用PropertyConfigurator来解析。
1.4、继续跟进((Configurator)configurator).doConfigure(url, hierarchy);到PropertyConfigurator类的doConfigure()方法。
public void doConfigure(Properties properties, LoggerRepository hierarchy) {
this.repository = hierarchy;
String value = properties.getProperty("log4j.debug");
if (value == null) {
value = properties.getProperty("log4j.configDebug");
if (value != null) {
LogLog.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead.");
}
}
if (value != null) {
LogLog.setInternalDebugging(OptionConverter.toBoolean(value, true));
}
String reset = properties.getProperty("log4j.reset");
if (reset != null && OptionConverter.toBoolean(reset, false)) {
hierarchy.resetConfiguration();
}
String thresholdStr = OptionConverter.findAndSubst("log4j.threshold", properties);
if (thresholdStr != null) {
hierarchy.setThreshold(OptionConverter.toLevel(thresholdStr, Level.ALL));
LogLog.debug("Hierarchy threshold set to [" + hierarchy.getThreshold() + "].");
}
this.configureRootCategory(properties, hierarchy);
this.configureLoggerFactory(properties);
this.parseCatsAndRenderers(properties, hierarchy);
LogLog.debug("Finished configuring.");
this.registry.clear();
}
1.4.1、核心代码: 配置根logger及其appender。
this.configureRootCategory(properties, hierarchy);
void parseCategory(Properties props, Logger logger, String optionKey, String loggerName, String value) {
LogLog.debug("Parsing for [" + loggerName + "] with value=[" + value + "].");
StringTokenizer st = new StringTokenizer(value, ",");
if (!value.startsWith(",") && !value.equals("")) {
if (!st.hasMoreTokens()) {
return;
}
String levelStr = st.nextToken();
LogLog.debug("Level token is [" + levelStr + "].");
if (!"inherited".equalsIgnoreCase(levelStr) && !"null".equalsIgnoreCase(levelStr)) {
logger.setLevel(OptionConverter.toLevel(levelStr, Level.DEBUG));
} else if (loggerName.equals("root")) {
LogLog.warn("The root logger cannot be set to null.");
} else {
logger.setLevel((Level)null);
}
LogLog.debug("Category " + loggerName + " set to " + logger.getLevel());
}
logger.removeAllAppenders();
//遍历log4j.rootLogger中的appenderName
while(st.hasMoreTokens()) {
String appenderName = st.nextToken().trim();
if (appenderName != null && !appenderName.equals(",")) {
LogLog.debug("Parsing appender named \"" + appenderName + "\".");
//核心代码:根据appenderName解析出appender
Appender appender = this.parseAppender(props, appenderName);
if (appender != null) {
//将appender加入到logger的AppenderAttachableImpl的appenderList中
logger.addAppender(appender);
}
}
}
}
1.4.1.1、跟进logger.addAppender(appender);最终调用AppenderAttachableImpl类的addAppender方法。
public void addAppender(Appender newAppender) {
if (newAppender != null) {
if (this.appenderList == null) {
this.appenderList = new Vector(1);
}
if (!this.appenderList.contains(newAppender)) {
this.appenderList.addElement(newAppender);
}
}
}
1.4.1.2、跟进Appender appender = this.parseAppender(props, appenderName);
Appender parseAppender(Properties props, String appenderName) {
//根据appenderName获取registry缓存中的appender, 若存在则直接放回
Appender appender = this.registryGet(appenderName);
if (appender != null) {
LogLog.debug("Appender \"" + appenderName + "\" was already parsed.");
return appender;
} else {
String prefix = "log4j.appender." + appenderName;
//.layout在配置文件中是小写的
String layoutPrefix = prefix + ".layout";
appender = (Appender)OptionConverter.instantiateByKey(props, prefix, Appender.class, (Object)null);
if (appender == null) {
LogLog.error("Could not instantiate appender named \"" + appenderName + "\".");
return null;
} else {
appender.setName(appenderName);
if (appender instanceof OptionHandler) {
if (appender.requiresLayout()) {
Layout layout = (Layout)OptionConverter.instantiateByKey(props, layoutPrefix, Layout.class, (Object)null);
if (layout != null) {
appender.setLayout(layout);
LogLog.debug("Parsing layout options for \"" + appenderName + "\".");
//设置layout属性
PropertySetter.setProperties(layout, props, layoutPrefix + ".");
LogLog.debug("End of parsing for \"" + appenderName + "\".");
}
}
//初始化异常处理器
String errorHandlerPrefix = prefix + ".errorhandler";
String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, props);
if (errorHandlerClass != null) {
ErrorHandler eh = (ErrorHandler)OptionConverter.instantiateByKey(props, errorHandlerPrefix, ErrorHandler.class, (Object)null);
if (eh != null) {
appender.setErrorHandler(eh);
LogLog.debug("Parsing errorhandler options for \"" + appenderName + "\".");
this.parseErrorHandler(eh, errorHandlerPrefix, props, this.repository);
Properties edited = new Properties();
String[] keys = new String[]{errorHandlerPrefix + "." + "root-ref", errorHandlerPrefix + "." + "logger-ref", errorHandlerPrefix + "." + "appender-ref"};
Iterator iter = props.entrySet().iterator();
while(true) {
if (!iter.hasNext()) {
PropertySetter.setProperties(eh, edited, errorHandlerPrefix + ".");
LogLog.debug("End of errorhandler parsing for \"" + appenderName + "\".");
break;
}
Map.Entry entry = (Map.Entry)iter.next();
int i;
for(i = 0; i < keys.length && !keys[i].equals(entry.getKey()); ++i) {
}
if (i == keys.length) {
edited.put(entry.getKey(), entry.getValue());
}
}
}
}
//反射设置appender的其他属性
PropertySetter.setProperties(appender, props, prefix + ".");
LogLog.debug("Parsed \"" + appenderName + "\" options.");
}
this.parseAppenderFilters(props, appenderName, appender);
//加入registry缓存中
this.registryPut(appender);
return appender;
}
}
}
1.4.2、跟进this.configureLoggerFactory(properties);
protected void configureLoggerFactory(Properties props) {
String factoryClassName = OptionConverter.findAndSubst("log4j.loggerFactory", props);
if (factoryClassName != null) {
LogLog.debug("Setting category factory to [" + factoryClassName + "].");
this.loggerFactory = (LoggerFactory)OptionConverter.instantiateByClassName(factoryClassName, LoggerFactory.class, this.loggerFactory);
PropertySetter.setProperties(this.loggerFactory, props, "log4j.factory.");
}
}
1.4.2.1、获取配置文件loggerFactory全限定名, 创建loggerFactory实例。
1.4.2.2、反射设置loggerFactory的属性,注: 不配置时, loggerFactory使用默认值, 为DefaultCategoryFactory实例对象. loggerFactory用于创建自定义的logger对象。
1.4.3、跟进this.parseCatsAndRenderers(properties, hierarchy);主要是父子logger的绑定和设置logger的additive属性。
protected void parseCatsAndRenderers(Properties props, LoggerRepository hierarchy) {
Enumeration enumeration = props.propertyNames();
while(true) {
while(enumeration.hasMoreElements()) {
String key = (String)enumeration.nextElement();
String loggerName;
String value;
if (!key.startsWith("log4j.category.") && !key.startsWith("log4j.logger.")) {
if (key.startsWith("log4j.renderer.")) {
loggerName = key.substring("log4j.renderer.".length());
value = OptionConverter.findAndSubst(key, props);
if (hierarchy instanceof RendererSupport) {
RendererMap.addRenderer((RendererSupport)hierarchy, loggerName, value);
}
} else if (key.equals("log4j.throwableRenderer") && hierarchy instanceof ThrowableRendererSupport) {
ThrowableRenderer tr = (ThrowableRenderer)OptionConverter.instantiateByKey(props, "log4j.throwableRenderer", ThrowableRenderer.class, (Object)null);
if (tr == null) {
LogLog.error("Could not instantiate throwableRenderer.");
} else {
PropertySetter setter = new PropertySetter(tr);
setter.setProperties(props, "log4j.throwableRenderer.");
((ThrowableRendererSupport)hierarchy).setThrowableRenderer(tr);
}
}
} else {
loggerName = null;
if (key.startsWith("log4j.category.")) {
loggerName = key.substring("log4j.category.".length());
} else if (key.startsWith("log4j.logger.")) {
loggerName = key.substring("log4j.logger.".length());
}
value = OptionConverter.findAndSubst(key, props);
//核心代码: 创建出自定义logger对象, 绑定其与其他logger的关系
Logger logger = hierarchy.getLogger(loggerName, this.loggerFactory);
synchronized(logger) {
this.parseCategory(props, logger, key, loggerName, value);
this.parseAdditivityForLogger(props, logger, loggerName);
}
}
}
return;
}
}