注意: 代码中 ‘…’ 符号表示省略次要代码
一、 流程总结
- 通过Logger.getLogger()获取Logger,调用LogManager静态代码块加载。
- LogManager内部依次判断xml、properties,直到发现为止,并进行资源加载转换为URL格式。
- 接着通过PropertyConfigurator类依次进行Append、Layout及其属性设置并挂载到RootLogger上。
二、步骤详细分析
1、LogManager静态代码块分析
LogManager是Log4j中的核心管理器。
//获取Logger
Logger log = Logger.getLogger(this.getClass());
//实际通过LogManager获取,从而引入LogManager
public static Logger getLogger(Class clazz) {
return LogManager.getLogger(clazz.getName());
}
//LogManager类中存在静态代码块
static{
...
//获取配置文件
URL url = null;
if (configurationOptionStr == null) {
url = Loader.getResource("log4j.xml");
if (url == null) {
url = Loader.getResource("log4j.properties");
}
}
...
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);
}
}
}
加载配置文件
//进入OptionConverter类中的selectAndConfigure方法中
public static void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy) {
...
configurator = new PropertyConfigurator();
...
((Configurator)configurator).doConfigure(url, hierarchy);
}
//进入PropertyConfigurator类内部的doConfigure
public void doConfigure(URL configURL, LoggerRepository hierarchy) {
//读取资源文件并存储到中Properties
Properties props = new Properties();
...
uConn = configURL.openConnection();
uConn.setUseCaches(false);
istream = uConn.getInputStream();
props.load(istream);
break label103;
...
this.doConfigure(props, hierarchy);
}
//进入其内部的重载方法doConfigure
public void doConfigure(Properties properties, LoggerRepository hierarchy) {
...
//配置根节点
this.configureRootCategory(properties, hierarchy);
this.configureLoggerFactory(properties);
this.parseCatsAndRenderers(properties, hierarchy);
LogLog.debug("Finished configuring.");
this.registry.clear();
}
//进入内部的configureRootCategory方法
void configureRootCategory(Properties props, LoggerRepository hierarchy) {
String effectiveFrefix = "log4j.rootLogger";
...
/*
获取RootLogger根节点,Log4j内部结点已树结构存储,
RootLogger作为根节点,子节点具有继承父节点特性
*/
Logger root = hierarchy.getRootLogger();
synchronized(root) {
this.parseCategory(props, root, effectiveFrefix, "root", value);
}
}
}
/*
进入parseCategory方法
逻辑分析:
1.分割符为','
2.根节点前缀为:log4j.rootLogger
3.第一个代表level等级,之后代表append自定义名称。
举例:log4j.rootLogger=info,console
4.解析appender,存在并将其挂载到rootlogger内部的appenders链表上
*/
void parseCategory(Properties props, Logger logger, String optionKey, String loggerName, String value) {
//分隔符为','
StringTokenizer st = new StringTokenizer(value, ",");
if (!value.startsWith(",") && !value.equals("")) {
if (!st.hasMoreTokens()) {
return;
}
String levelStr = st.nextToken();
...
if (!"inherited".equalsIgnoreCase(levelStr) && !"null".equalsIgnoreCase(levelStr)) {
//设置level
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);
}
}
//添加appender之前,先清空
logger.removeAllAppenders();
//循环注册append到根节点上
while(st.hasMoreTokens()) {
String appenderName = st.nextToken().trim();
if (appenderName != null && !appenderName.equals(",")) {
//解析appender并挂载到logger中的appenders链表中
Appender appender = this.parseAppender(props, appenderName);
if (appender != null) {
logger.addAppender(appender);
}
}
}
}
//解析appender
/*
逻辑分析:
1.appender键名为log4j.appender.自定义别名
例如:log4j.appender.console=org.apache.log4j.ConsoleAppender
2.实例化appender
3.设置相应layout
4.设置layout属性,例如PatternLayout内部的pattern【打印输出格式】
*/
Appender parseAppender(Properties props, String appenderName) {
Appender appender = this.registryGet(appenderName);
//检测是否已加载
if (appender != null) {
LogLog.debug("Appender \"" + appenderName + "\" was already parsed.");
return appender;
} else {
//未加载则实例化相应appender对象
String prefix = "log4j.appender." + appenderName;
String layoutPrefix = prefix + ".layout";
//实例化appender
appender = (Appender)OptionConverter.instantiateByKey(props, prefix, Appender.class, (Object)null);
//若实例化失败则取消相应挂载的appender
if (appender == null) {
LogLog.error("Could not instantiate appender named \"" + appenderName + "\".");
return null;
} else {
appender.setName(appenderName);
if (appender instanceof OptionHandler) {
/*
配置layout,前缀为:log4j.appender.自定义别名.layout
例如:
log4j.appender.console.layout=org.apache.log4j.PatternLayout
*/
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内部属性,
键名key为:log4j.appender.自定义别名.layout.属性
例如:【配置PatternLayout内部pattern属性,打印格式】
log4j.appender.pattern.layout.conversionPattern=%r [%t] %p %c %x - %m%n
*/
PropertySetter.setProperties(layout, props, layoutPrefix + ".");
LogLog.debug("End of parsing for \"" + appenderName + "\".");
}
}
...
this.parseAppenderFilters(props, appenderName, appender);
//将获取到的append对象注册到内部,防止重复注册
this.registryPut(appender);
return appender;
}
}
}