log4j 源码解析_log4j1.x设置自动加载log4j.xml
在没有设置自动加载log4j.xml的时候,一般我们需要这么处理
static void initLogger()
{System.out.println("configurating log4j with log4j xml");DOMConfigurator.configure("log4j.xml");logger = Logger.getLogger("name");
}
此时如果在eclipse中,log4j.xml是与src工程目录并列的位置;
如果工程编译成jar包,log4j.xml也是jar包在同一目录下使用,不会包含在jar包当中;
我们其实还可以将log4j的读取过程通过log4j的自动加载机制完成,省去第一行代码
DOMConfigurator.configure("log4j.xml");;
我们只需要把log4j.xml的位置挪动下,挪动到src代码目录里层即可;
下面介绍下一般方式下的加载原理:
org.apache.log4j.xml.DOMConfigurator.java源码如下
/**
A static version of {@link #doConfigure(String, LoggerRepository)}.
*/
static
public
void configure(String filename) throws FactoryConfigurationError
{new DOMConfigurator().doConfigure(filename, LogManager.getLoggerRepository());
}public
void doConfigure(final String filename, LoggerRepository repository)
{ParseAction action = new ParseAction() {public Document parse(final DocumentBuilder parser) throws SAXException, IOException {return parser.parse(new File(filename));}public String toString() { return "file [" + filename + "]"; }};doConfigure(action, repository);}private final void doConfigure(final ParseAction action,final LoggerRepository repository)throws FactoryConfigurationError
{DocumentBuilderFactory dbf = null;this.repository = repository;try { LogLog.debug("System property is :"+OptionConverter.getSystemProperty(dbfKey, null)); dbf = DocumentBuilderFactory.newInstance();LogLog.debug("Standard DocumentBuilderFactory search succeded.");LogLog.debug("DocumentBuilderFactory is: "+dbf.getClass().getName());} catch(FactoryConfigurationError fce) {Exception e = fce.getException();LogLog.debug("Could not instantiate a DocumentBuilderFactory.", e);throw fce;}try {dbf.setValidating(true);DocumentBuilder docBuilder = dbf.newDocumentBuilder();docBuilder.setErrorHandler(new SAXErrorHandler()); docBuilder.setEntityResolver(new Log4jEntityResolver());Document doc = action.parse(docBuilder); parse(doc.getDocumentElement());} catch (Exception e) {if (e instanceof InterruptedException || e instanceof InterruptedIOException){Thread.currentThread().interrupt();}// I know this is miserable...LogLog.error("Could not parse "+ action.toString() + ".", e);}}
这样就开始了log4j.xml的解析过程,具体过程不表,那是另外一个问题;
我们注意到:
LogManager.getLoggerRepository()返回的
org.apache.log4j.spi.LoggerRepository repository这个对象对象
org.apache.log4j.spi.LoggerRepository 接口如下,顾名思义,就是存放Logger的仓库
package org.apache.log4j.spi;import java.util.Enumeration;import org.apache.log4j.Appender;
import org.apache.log4j.Category;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;/**A <code>LoggerRepository</code> is used to create and retrieve<code>Loggers</code>. The relation between loggers in a repositorydepends on the repository but typically loggers are arranged in anamed hierarchy.<p>In addition to the creational methods, a<code>LoggerRepository</code> can be queried for existing loggers,can act as a point of registry for events related to loggers. @author Ceki Gülcü@since 1.2 */
public interface LoggerRepository {/**Add a {@link HierarchyEventListener} event to the repository.*/publicvoid addHierarchyEventListener(HierarchyEventListener listener);/**Returns whether this repository is disabled for a givenlevel. The answer depends on the repository threshold and the<code>level</code> parameter. See also {@link #setThreshold}method. */boolean isDisabled(int level);/**Set the repository-wide threshold. All logging requests below thethreshold are immediately dropped. By default, the threshold isset to <code>Level.ALL</code> which has the lowest possible rank. */publicvoid setThreshold(Level level);/**Another form of {@link #setThreshold(Level)} accepting a stringparameter instead of a <code>Level</code>. */publicvoid setThreshold(String val);publicvoid emitNoAppenderWarning(Category cat);/**Get the repository-wide threshold. See {@link#setThreshold(Level)} for an explanation. */publicLevel getThreshold();publicLogger getLogger(String name);publicLogger getLogger(String name, LoggerFactory factory);publicLogger getRootLogger();publicabstractLogger exists(String name);publicabstractvoid shutdown();publicEnumeration getCurrentLoggers();/**Deprecated. Please use {@link #getCurrentLoggers} instead. */publicEnumeration getCurrentCategories();publicabstractvoid fireAddAppenderEvent(Category logger, Appender appender);publicabstractvoid resetConfiguration();}
而在org.apache.log4j.LogManager类当中
static public LoggerRepository getLoggerRepository()
{if (repositorySelector == null) {repositorySelector = new DefaultRepositorySelector(new NOPLoggerRepository());guard = null;Exception ex = new IllegalStateException("Class invariant violation");String msg ="log4j called after unloading, see http://logging.apache.org/log4j/1.2/faq.html#unload.";if (isLikelySafeScenario(ex)) {LogLog.debug(msg, ex);} else {LogLog.error(msg, ex);}}return repositorySelector.getLoggerRepository();
}
而LogManager中的org.apache.log4j.RepositorySelector对象执行到这里到底是不是null呢?
事实上并不是null
首先看下两个static的常量字符串的定义
static public final String DEFAULT_CONFIGURATION_FILE = "log4j.properties";static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";public static final String DEFAULT_INIT_OVERRIDEE_KEY="log4j.defaultInitOverride"
可以看下LogManager的static代码块
static {// By default we use a DefaultRepositorySelector which always returns 'h'.Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG));repositorySelector = new DefaultRepositorySelector(h);/** Search for the properties file log4j.properties in the CLASSPATH. */String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,null);// if there is no default init override, then get the resource// specified by the user or the default config file.if(override == null || "false".equalsIgnoreCase(override)) {String configurationOptionStr = OptionConverter.getSystemProperty(DEFAULT_CONFIGURATION_KEY, null);String configuratorClassName = OptionConverter.getSystemProperty(CONFIGURATOR_CLASS_KEY, null); URL url = null;// if the user has not specified the log4j.configuration// property, we search first for the file "log4j.xml" and then// "log4j.properties"if(configurationOptionStr == null) { url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);if(url == null) {url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);}} else {try {url = new URL(configurationOptionStr);} catch (MalformedURLException ex) {// so, resource is not a URL:// attempt to get the resource from the class pathurl = Loader.getResource(configurationOptionStr); } }// If we have a non-null url, then delegate the rest of the// configuration to the OptionConverter.selectAndConfigure// method.if(url != null) {LogLog.debug("Using URL ["+url+"] for automatic log4j configuration.");try {OptionConverter.selectAndConfigure(url, configuratorClassName,LogManager.getLoggerRepository());} catch (NoClassDefFoundError e) {LogLog.warn("Error during default initialization", e);}} else {LogLog.debug("Could not find resource: ["+configurationOptionStr+"].");}} else {LogLog.debug("Default initialization of overridden by " + DEFAULT_INIT_OVERRIDE_KEY + "property."); }
}
我们知道在加载类的过程中首先会执行该类的static变量的初始化以及static代码块逻辑;
// By default we use a DefaultRepositorySelector which always returns 'h'.
Hierarchy h =newHierarchy(new RootLogger((Level) Level.DEBUG));
repositorySelector = new DefaultRepositorySelector(h);
Hierarchy类是RepositorySelector类的实现,
应为意思为层级的意思,也正符合Logger这种树形结构关系;
这样其实LogManager中的repositorySelector对象已经被初始化完毕了;
至此我们印证了LogManager.getLoggerRepository()中的返回值在static代码执行的时候就被初始化了;
后面的代码其实就是我们上面提到的自动加载机制;
首先我们来看下org.apache.log4j.xml.helpers.OptionConverter类的getSystemProperty()方法
/**Very similar to <code>System.getProperty</code> exceptthat the {@link SecurityException} is hidden.@param key The key to search for.@param def The default value to return.@return the string value of the system property, or the defaultvalue if there is no property with that key.@since 1.1 */publicstaticString getSystemProperty(String key, String def) {try {return System.getProperty(key, def);} catch(Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionExLogLog.debug("Was not allowed to read system property ""+key+"".");return def;}}
自然我们没有设置这个值,所以就从默认的路径去寻找我们希望自动加载的log4j.xml的配置文件
自动加载机制会自动去Class路径上寻找log4j.xml log4j.properties文件
利用类加载的ClassLoader来寻找资源
所以
if(override == null || "false".equalsIgnoreCase(override))
后面再次进入if条件语句
if(configurationOptionStr == null)
if(configurationOptionStr == null) { url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);if(url == null) {url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);}
}
截图中的找不到资源的日志也是上面的Loader类中的getResource代码逻辑;
注意:这个log日志默认是不打印的,需要提前设置
System.setProperty("log4j.debug","true");
我们来看下org.apache.log4j.helpers.Loader类中的getResource方法
static public URL getResource(String resource) {ClassLoader classLoader = null;URL url = null;try {if(!java1 && !ignoreTCL) {classLoader = getTCL();if(classLoader != null) {LogLog.debug("Trying to find ["+resource+"] using context classloader "+classLoader+".");url = classLoader.getResource(resource); if(url != null) {return url;}}}// We could not find resource. Ler us now try with the// classloader that loaded this class.classLoader = Loader.class.getClassLoader(); if(classLoader != null) {LogLog.debug("Trying to find ["+resource+"] using "+classLoader+" class loader.");url = classLoader.getResource(resource);if(url != null) {return url;}}} catch(IllegalAccessException t) {LogLog.warn(TSTR, t);} catch(InvocationTargetException t) {if (t.getTargetException() instanceof InterruptedException|| t.getTargetException() instanceof InterruptedIOException) {Thread.currentThread().interrupt();}LogLog.warn(TSTR, t);} catch(Throwable t) {//// can't be InterruptedException or InterruptedIOException// since not declared, must be error or RuntimeError.LogLog.warn(TSTR, t);}// Last ditch attempt: get the resource from the class path. It// may be the case that clazz was loaded by the Extentsion class// loader which the parent of the system class loader. Hence the// code below.LogLog.debug("Trying to find ["+resource+"] using ClassLoader.getSystemResource().");return ClassLoader.getSystemResource(resource);}
期间在考虑需要把log4j.xml放置到什么位置上的问题。
关于Class.getResource和ClassLoader.getResource的路径问题 - yejg1212 - 博客园www.cnblogs.com
参看了这篇文章;
不过文章对于ClassLoader的getResource的路径描述是错误的。
由于log4j的自动加载机制就是用的ClassLoader的getResource方法,需要并且是不带"/"
的,事实上ClassLoader你使用"/"的参数会直接报错,默认路径就在工程的根目录;
总结下:
Log4j1.x 加载配置文件过程:
1.log4j1.x启动时,默认会寻找类路径下的log4j.xml配置文件,若没有,会寻找log4j.properties文件。
2.如果不想走默认的路径,可以在程序中指定配置文件路径。
类似于这种:
static Logger logger = null;
static
{
//此时指定的是src同级目录亦jar包同级目录
DOMConfigurator.configure("log4j.xml");
logger = Logger.getRootLogger();
}
3.如果以上两种方式均未指定,仍强行使用Logger,编译并不会报错,只是会在使用logger时在console打印红色警告。
log4j:WARN No appenders could be found for logger (xxx).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
log4j 源码解析_log4j1.x设置自动加载log4j.xml相关推荐
- thinkphp源码分析(三)—自动加载篇(Loader的分析)
源码分析 自动加载 系统会调用 Loader::register()方法注册自动加载,在这一步完成后,所有符合规范的类库(包括Composer依赖加载的第三方类库)都将自动加载. 系统的自动加载由下面 ...
- 一个mapper接口有多个mapper.xml 文件_MyBatis 源码解析:映射文件的加载与解析(上)
上一篇我们分析了配置文件的加载与解析过程,本文将继续对映射文件的加载与解析实现进行分析.MyBatis 的映射文件用于配置 SQL 语句.二级缓存,以及结果集映射等,是区别于其它 ORM 框架的主要特 ...
- jQuery源码解析(3)—— ready加载、queue队列
ready.queue放在一块写,没有特殊的意思,只是相对来说它俩可能源码是最简单的了.ready是在dom加载完成后,以最快速度触发,很实用.queue是队列,比如动画的顺序触发就是通过默认队列'f ...
- log4j源码解析及一个log4j:ERROR Attempted to append to closed appender named 的问题
一.log4j源码解析 最近出现一个问题,弄得我不得不把log4j的源代码读了一篇. 如果自己不想写代码,可以下载此代码:https://github.com/lwwcl1314/atlantis/t ...
- Mybatis 源码分析(一)配置文件加载流程
Mybatis 源码分析(一)配置文件加载流程 1.项目构建 引入依赖 <dependency><groupId>org.mybatis</groupId>< ...
- 【SA8295P 源码分析】08 - XBL Loader 加载 SMSS、XBL Config、SHRM、CDT 、DDR、APDP、RamDump、OEM_MISC、AOP、QSEE过程分析
[SA8295P 源码分析]08 - XBL Loader 加载 SMSS.XBL Config.SHRM.CDT .DDR.APDP.RamDump.OEM_MISC.AOP.QSEE Dev Co ...
- web.xml的contextConfigLocation作用及自动加载applicationContext.xml
web.xml的contextConfigLocation作用及自动加载applicationContext.xml 转自:http://blog.csdn.net/sapphire_aling/ar ...
- log4j 源码解析_Log4j源码解析--框架流程+核心解析
OK,现在我们来研究Log4j的源码: 这篇博客有参照上善若水的博客,原文出处:http://www.blogjava.net/DLevin/archive/2012/06/28/381667.htm ...
- log4j 源码解析_log4j2源码解析(2)--LoggerContext
LoggerContext作用及初始化流程 根据我们在Log4j初识中的实例可以看出,在不适用日志门面插件slf4j的情况下,获取logger的方式一般为 Logger logger = logMan ...
最新文章
- 福利丨网友授课视频分享:机器学习实战-KNN-第一部分
- Java接口多线程并发测试 (一)
- django中csrftoken跨站请求伪造的几种方式
- 动态新增元素的js无效的解决方法
- mysql 视图 字符集_MySQL创建子视图并查看的时候,字符集报错问题
- leetcode 790. Domino and Tromino Tiling | 790. 多米诺和托米诺平铺(暴力递归->DP)
- To B生意的私域流量,你应该知道的10件事儿
- 为什么先编码再解码? 即先降采样,然后上采样
- sql server数据库中 smallint, int ,bigint ,tinyint的区别与长度
- 软件评测师教程简介(第一篇-理论篇)
- visio2016专业版2018最新密钥和下载方法 整理
- 对于i=1,i=i++,最后输出结果为1的理解
- AlibabaCloud
- NUAA-泛在网实验-实验六
- PostgreSQL 179个场景
- 安卓网页自动填充_敏感插件限时显示提供!自动填写网盘提取码,且用且珍惜...
- AAAI2020|Asymmetric Co-Teaching for Unsupervised Cross-Domain Person Re-Identification
- 下载网盘资源如何更快呢?
- 如何估算一个城市有多少座加油站
- Git提示nothing to commit, working tree clean
热门文章
- 恭喜你们,碰到了问题
- 小型化RDA5807调频收音模块实验板
- 听说你有病,我这儿可有对症的药
- java华农组合模式,华农《数据库应用》往年考试例卷
- python空字典对象相当于false吗_python怎么判断某一对象是否为字典
- android 固定底部导航,Android如何实现底部菜单固定到底部
- 怎么看rabbitmq的浏览器信息_买房沙盘怎么看?沙盘也可以看出很多信息的
- excel引用指定单元格数据_Excel数据查找引用函数详解,一看就会用 #办公技巧 #excel #职场
- 小程序在输入npm命令_微信小程序使用npm包步骤
- FPGA从Xilinx的7系列学起(6)