在没有设置自动加载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&uuml;lc&uuml;@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相关推荐

  1. thinkphp源码分析(三)—自动加载篇(Loader的分析)

    源码分析 自动加载 系统会调用 Loader::register()方法注册自动加载,在这一步完成后,所有符合规范的类库(包括Composer依赖加载的第三方类库)都将自动加载. 系统的自动加载由下面 ...

  2. 一个mapper接口有多个mapper.xml 文件_MyBatis 源码解析:映射文件的加载与解析(上)

    上一篇我们分析了配置文件的加载与解析过程,本文将继续对映射文件的加载与解析实现进行分析.MyBatis 的映射文件用于配置 SQL 语句.二级缓存,以及结果集映射等,是区别于其它 ORM 框架的主要特 ...

  3. jQuery源码解析(3)—— ready加载、queue队列

    ready.queue放在一块写,没有特殊的意思,只是相对来说它俩可能源码是最简单的了.ready是在dom加载完成后,以最快速度触发,很实用.queue是队列,比如动画的顺序触发就是通过默认队列'f ...

  4. log4j源码解析及一个log4j:ERROR Attempted to append to closed appender named 的问题

    一.log4j源码解析 最近出现一个问题,弄得我不得不把log4j的源代码读了一篇. 如果自己不想写代码,可以下载此代码:https://github.com/lwwcl1314/atlantis/t ...

  5. Mybatis 源码分析(一)配置文件加载流程

    Mybatis 源码分析(一)配置文件加载流程 1.项目构建 引入依赖 <dependency><groupId>org.mybatis</groupId>< ...

  6. 【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 ...

  7. web.xml的contextConfigLocation作用及自动加载applicationContext.xml

    web.xml的contextConfigLocation作用及自动加载applicationContext.xml 转自:http://blog.csdn.net/sapphire_aling/ar ...

  8. log4j 源码解析_Log4j源码解析--框架流程+核心解析

    OK,现在我们来研究Log4j的源码: 这篇博客有参照上善若水的博客,原文出处:http://www.blogjava.net/DLevin/archive/2012/06/28/381667.htm ...

  9. log4j 源码解析_log4j2源码解析(2)--LoggerContext

    LoggerContext作用及初始化流程 根据我们在Log4j初识中的实例可以看出,在不适用日志门面插件slf4j的情况下,获取logger的方式一般为 Logger logger = logMan ...

最新文章

  1. 福利丨网友授课视频分享:机器学习实战-KNN-第一部分
  2. Java接口多线程并发测试 (一)
  3. django中csrftoken跨站请求伪造的几种方式
  4. 动态新增元素的js无效的解决方法
  5. mysql 视图 字符集_MySQL创建子视图并查看的时候,字符集报错问题
  6. leetcode 790. Domino and Tromino Tiling | 790. 多米诺和托米诺平铺(暴力递归->DP)
  7. To B生意的私域流量,你应该知道的10件事儿
  8. 为什么先编码再解码? 即先降采样,然后上采样
  9. sql server数据库中 smallint, int ,bigint ,tinyint的区别与长度
  10. 软件评测师教程简介(第一篇-理论篇)
  11. visio2016专业版2018最新密钥和下载方法 整理
  12. 对于i=1,i=i++,最后输出结果为1的理解
  13. AlibabaCloud
  14. NUAA-泛在网实验-实验六
  15. PostgreSQL 179个场景
  16. 安卓网页自动填充_敏感插件限时显示提供!自动填写网盘提取码,且用且珍惜...
  17. AAAI2020|Asymmetric Co-Teaching for Unsupervised Cross-Domain Person Re-Identification
  18. 下载网盘资源如何更快呢?
  19. 如何估算一个城市有多少座加油站
  20. Git提示nothing to commit, working tree clean

热门文章

  1. 恭喜你们,碰到了问题
  2. 小型化RDA5807调频收音模块实验板
  3. 听说你有病,我这儿可有对症的药
  4. java华农组合模式,华农《数据库应用》往年考试例卷
  5. python空字典对象相当于false吗_python怎么判断某一对象是否为字典
  6. android 固定底部导航,Android如何实现底部菜单固定到底部
  7. 怎么看rabbitmq的浏览器信息_买房沙盘怎么看?沙盘也可以看出很多信息的
  8. excel引用指定单元格数据_Excel数据查找引用函数详解,一看就会用 #办公技巧 #excel #职场
  9. 小程序在输入npm命令_微信小程序使用npm包步骤
  10. FPGA从Xilinx的7系列学起(6)