java加载log4j.xml_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 LoggerRepository is used to create and retrieveLoggers. The relation between loggers in a repositorydepends on the repository but typically loggers are arranged in anamed hierarchy.

In addition to the creational methods, aLoggerRepository 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.*/

public

void addHierarchyEventListener(HierarchyEventListener listener);

/**Returns whether this repository is disabled for a givenlevel. The answer depends on the repository threshold and thelevel 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 Level.ALL which has the lowest possible rank. */

public

void setThreshold(Level level);

/**Another form of {@link #setThreshold(Level)} accepting a stringparameter instead of a Level. */

public

void setThreshold(String val);

public

void emitNoAppenderWarning(Category cat);

/**Get the repository-wide threshold. See {@link#setThreshold(Level)} for an explanation. */

public

Level getThreshold();

public

Logger getLogger(String name);

public

Logger getLogger(String name, LoggerFactory factory);

public

Logger getRootLogger();

public

abstract

Logger exists(String name);

public

abstract

void shutdown();

public

Enumeration getCurrentLoggers();

/**Deprecated. Please use {@link #getCurrentLoggers} instead. */

public

Enumeration getCurrentCategories();

public

abstract

void fireAddAppenderEvent(Category logger, Appender appender);

public

abstract

void 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, seehttp://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 path url = 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 System.getProperty 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 */

public

static

String getSystemProperty(String key, String def) {

try {

return System.getProperty(key, def);

} catch(Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx LogLog.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.com594d73c9dbbeb591cdb683e34c8a3266.png

参看了这篇文章;

不过文章对于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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值