Log4j源码分析系列——配置文件(logg4j.properties)读取

注意: 代码中 ‘…’ 符号表示省略次要代码

一、 流程总结

  1. 通过Logger.getLogger()获取Logger,调用LogManager静态代码块加载
  2. LogManager内部依次判断xml、properties,直到发现为止,并进行资源加载转换为URL格式。
  3. 接着通过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;
            }
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值