Log4j源码解析--LoggerRepository和Configurator解析


本文转自上善若水的博客,原文出处:http://www.blogjava.net/DLevin/archive/2012/07/10/382678.html。感谢做者的无私分享。html

LoggerRepository从字面上理解,它是一个Logger的容器,它会建立并缓存Logger实例,从而具备相同名字的Logger实例不会屡次建立,以提升性能。它的这种特性有点相似SpringIOC概念。Log4J支持两种配置文件:properties文件和xml文件。Configurator解析配置文件,并将解析后的信息添加到LoggerRepository中。LogManager最终将LoggerRepositoryConfigurator整合在一块儿。java

LoggerRepository接口

LoggerRepository是一个Logger的容器,它负责建立、缓存Logger实例,同时它也维护了Logger之间的关系,由于在Log4J中,全部Logger都组装成以RootLogger为根的一棵树,树的层次由LoggerName来决定,其中以’.’分隔。apache

除了作为一个Logger容器,它还有一个Threshold属性,用于过滤全部在Threshold级别如下的日志。以及其余和Logger操做相关的方法和属性。
缓存

LoggerRepository的接口定义以下:
app

 1  public  interface LoggerRepository {
 2      public  void addHierarchyEventListener(HierarchyEventListener listener);
 3      boolean isDisabled( int level);
 4      public  void setThreshold(Level level);
 5      public  void setThreshold(String val);
 6      public  void emitNoAppenderWarning(Category cat);
 7      public Level getThreshold();
 8      public Logger getLogger(String name);
 9      public Logger getLogger(String name, LoggerFactory factory);
10      public Logger getRootLogger();
11      public  abstract Logger exists(String name);
12      public  abstract  void shutdown();
13      public Enumeration getCurrentLoggers();
14      public  abstract  void fireAddAppenderEvent(Category logger, Appender appender);
15      public  abstract  void resetConfiguration();
16 }

 

Hierarchy

HierarchyLog4J中默认对LoggerRepository的实现类,它用于表达其内部的Logger是以层次结构存储的。在对LoggerRepository接口的实现中,getLogger()方法是其最核心的实现,于是首先从这个方法开始。less

Hierarchy中用一个Hashtable来存储全部Logger实例,它以CategoryKey做为keyLogger做为value,其中CategoryKey是对LoggerName字符串的封装,之因此要引入这个类是出于性能考虑,由于它会缓存Name字符串的hash code,这样在查找过程当中计算hash code时就能够直接取得而不用每次都计算。ide

 1  class CategoryKey {
 2     String name;
 3      int hashCache;
 4 
 5     CategoryKey(String name) {
 6          this.name = name;
 7         hashCache = name.hashCode();
 8     }
 9      final  public  int hashCode() {
10          return hashCache;
11     }
12      final  public  boolean equals(Object rArg) {
13          if ( this == rArg)
14              return  true;
15          if (rArg !=  null && CategoryKey. class == rArg.getClass())
16              return name.equals(((CategoryKey) rArg).name);
17          else
18              return  false;
19     }
20 }

getLogger()方法中有一个重载函数提供LoggerFactory接口,它用于没有在LoggerRepository中找到Logger实例时建立相应的Logger实例,默认实现直接建立一个Logger实例,用户能够经过自定义LoggerFactory实现建立本身的Logger实例。函数

1  public  interface LoggerFactory {
2      public Logger makeNewLoggerInstance(String name);
3 }
4  class DefaultCategoryFactory  implements LoggerFactory {
5      public Logger makeNewLoggerInstance(String name) {
6          return  new Logger(name);
7     }
8 }

getLogger()方法首先根据传入name建立CategoryKey实例,然后从缓存ht字段中查找:oop

1.       若是找到对应的Logger实例,则直接返回该实例。性能

2.       若是没有找到任何实例,则使用LoggerFactory建立新的Logger实例,并将该实例缓存到ht集合中,同时更新新建立Logger实例的parent属性。更新parent属性最简单的作法是从后往前以’.’为分隔符截取字符串,使用截取后的字符串从ht集合中查找是否存在Logger实例,若是存在,则新建立的Logger实例的parent即为找到的实例,若在整个遍历过程当中都没有找到相应的parent实例,则其parent实例为root。然而若是一个“x.y.z.wLogger起初的parent设置为root,然后出现“x.y.zLogger实例,那么就须要更新“x.y.z.wLoggerparent为“x.y.zLogger实例,此时就会遇到一个如何找到在集合中已经存在的“x.y.zLogger实例子节点的问题。固然一种简单的作法是遍历ht集合中全部实例,判断那个实例是否是“x.y.zLogger实例的子节点,是则更新其parent节点。因为每次的遍历会引发一些性能问题,于是Log4J使用ProvisionNode事先将全部的可能相关的子节点保存起来,并将ProvisionNode实例添加到ht集合中,这样只要找到对应的ProvisionNode实例,就能够找到全部相关的子节点了。好比对“x.y.z.wLogger实例,它会产生三个ProvisionNode实例(固然若是相应的实例已经存在,则直接添加而无需建立,另外,若是相应节点已是Logger实例,那么将“x.y.z.wLogger实例的parent直接指向它便可):ProvisionNode(“x”), ProvisionNode(“x.y”), ProvisionNode(“x.y.z”),他们都存储了“x.y.z.wLogger实例做为其子节点。

 1  class ProvisionNode  extends Vector {
 2     ProvisionNode(Logger logger) {
 3          super();
 4          this.addElement(logger);
 5     }
 6 }
 7  final  private  void updateParents(Logger cat) {
 8     String name = cat.name;
 9      int length = name.length();
10      boolean parentFound =  false;
11      //  if name = "x.y.z.w", loop thourgh "x.y.z", "x.y" and "x"
12       for ( int i = name.lastIndexOf('.', length - 1); i >= 0; i = name
13             .lastIndexOf('.', i - 1)) {
14         String substr = name.substring(0, i);
15         CategoryKey key =  new CategoryKey(substr); 
16         Object o = ht.get(key);
17          if (o ==  null) {
18             ProvisionNode pn =  new ProvisionNode(cat);
19             ht.put(key, pn);
20         }  else  if (o  instanceof Category) {
21             parentFound =  true;
22             cat.parent = (Category) o;
23              break//  no need to update the ancestors of the closest
24                       //  ancestor
25          }  else  if (o  instanceof ProvisionNode) {
26             ((ProvisionNode) o).addElement(cat);
27         }  else {
28             Exception e =  new IllegalStateException(
29                     "unexpected object type " + o.getClass() + " in ht.");
30             e.printStackTrace();
31         }
32     }
33      //  If we could not find any existing parents, then link with root.
34       if (!parentFound)
35         cat.parent = root;
36 }

3.   若是找到的是ProvisionNode实例,首先使用factory建立新的Logger实例,将该实例添加到ht集合中,而后更新找到的ProvisionNode内部全部Loggerparent字段以及新建立Loggerparent字段。更新过程当中须要注意ProvisionNode中的Logger实例已经指向了正确的parent了,因此只要更新那些ProvisionNodeLogger实例指向的parent比新建立的Logger自己层次要高的那些parent属性。好比开始插入“x.y.zLogger实例,然后插入“x.y.z.wLogger实例,此时ProvisionNode(“x”)认为“x.y.zLogger实例和“x.y.z.wLogger实例都是它的子节点,然后插入“xLogger实例,那么只须要更新“x.y.zLogger的父节点为“xLogger实例便可,而不用更新“x.y.z.wLogger实例的父节点。

 1  final  private  void updateChildren(ProvisionNode pn, Logger logger) {
 2      final  int last = pn.size();
 3      for ( int i = 0; i < last; i++) {
 4         Logger l = (Logger) pn.elementAt(i);
 5          //  Unless this child already points to a correct (lower) parent,
 6           //  make cat.parent point to l.parent and l.parent to cat.
 7           if (!l.parent.name.startsWith(logger.name)) {
 8             logger.parent = l.parent;
 9             l.parent = logger;
10         }
11     }
12 }

综合起来,getLogger()方法的实现代码以下:

 1  public Logger getLogger(String name, LoggerFactory factory) {
 2     CategoryKey key =  new CategoryKey(name);
 3     Logger logger;
 4      synchronized (ht) {
 5         Object o = ht.get(key);
 6          if (o ==  null) {
 7             logger = factory.makeNewLoggerInstance(name);
 8             logger.setHierarchy( this);
 9             ht.put(key, logger);
10             updateParents(logger);
11              return logger;
12         }  else  if (o  instanceof Logger) {
13              return (Logger) o;
14         }  else  if (o  instanceof ProvisionNode) {
15             logger = factory.makeNewLoggerInstance(name);
16             logger.setHierarchy( this);
17             ht.put(key, logger);
18             updateChildren((ProvisionNode) o, logger);
19             updateParents(logger);
20              return logger;
21         }  else {
22              //  It should be impossible to arrive here
23               return  null//  but let's keep the compiler happy.
24          }
25     }
26 }

其余的方法实现则比较简单,对LoggerRepository来讲,它也能够像其注册HierarchyEventListener监听器,每当向一个Logger添加或删除Appender,该监听器就会触发。

 1  public  interface HierarchyEventListener {
 2      public  void addAppenderEvent(Category cat, Appender appender);
 3      public  void removeAppenderEvent(Category cat, Appender appender);
 4 }
 5  private Vector listeners;
 6  public  void addHierarchyEventListener(HierarchyEventListener listener) {
 7      if (listeners.contains(listener)) {
 8         LogLog.warn("Ignoring attempt to add an existent listener.");
 9     }  else {
10         listeners.addElement(listener);
11     }
12 }
13  public  void fireAddAppenderEvent(Category logger, Appender appender) {
14      if (listeners !=  null) {
15          int size = listeners.size();
16         HierarchyEventListener listener;
17          for ( int i = 0; i < size; i++) {
18             listener = (HierarchyEventListener) listeners.elementAt(i);
19             listener.addAppenderEvent(logger, appender);
20         }
21     }
22 }
23  void fireRemoveAppenderEvent(Category logger, Appender appender) {
24      if (listeners !=  null) {
25          int size = listeners.size();
26         HierarchyEventListener listener;
27          for ( int i = 0; i < size; i++) {
28             listener = (HierarchyEventListener) listeners.elementAt(i);
29             listener.removeAppenderEvent(logger, appender);
30         }
31     }
32 }

Hierarchy中保存了threshold字段,用户能够设置threshold。而对root实例,它在够着Hierarchy时就被指定了。getCurrentLoggers()方法将ht集合中全部的Logger实例取出。shutdown()方法遍历全部Logger实例以及root实例,调用全部附加其上的Appenderclose()方法,并将全部Appender实例从Logger中移除,最后触发AppenderRemove事件。resetConfiguration()方法将root字段初始化、调用shutdown()方法移除Logger中的全部Appender、初始化全部Logger实例当不将其从LoggerRepository中移除、清楚rendererMapthrowableRender中的数据。

RendererSupport接口

RendererSupport接口支持用户为不一样的类设置相应的ObjectRender实例,从而能够从被渲染的类中或许更多的信息而不是默认的调用其toString()方法。

 1  public  interface RendererSupport {
 2      public RendererMap getRendererMap();
 3      public  void setRenderer(Class renderedClass, ObjectRenderer renderer);
 4 }
 5 Hierarchy类实现了RenderedSupprt接口,并且它的实现也很简单:
 6 RendererMap rendererMap;
 7  public Hierarchy(Logger root) {
 8     
 9     rendererMap =  new RendererMap();
10     
11 }
12  public  void addRenderer(Class classToRender, ObjectRenderer or) {
13     rendererMap.put(classToRender, or);
14 }
15  public RendererMap getRendererMap() {
16      return rendererMap;
17 }
18  public  void setRenderer(Class renderedClass, ObjectRenderer renderer) {
19     rendererMap.put(renderedClass, renderer);
20 }

RendererMap类实现中,它使用Hastable保存被渲染的类实例和相应的ObjectRender实例,在查找一个类是否存在注册的渲染类时,若是它自己没有找到,须要向上尝试其父类和接口是否有注册相应的ObjectRender类,若是都没有找到,则返回默认的ObjectRender

 1  public ObjectRenderer get(Class clazz) {
 2     ObjectRenderer r =  null;
 3      for (Class c = clazz; c !=  null; c = c.getSuperclass()) {
 4         r = (ObjectRenderer) map.get(c);
 5          if (r !=  null) {
 6              return r;
 7         }
 8         r = searchInterfaces(c);
 9          if (r !=  null)
10              return r;
11     }
12      return defaultRenderer;
13 }
14 ObjectRenderer searchInterfaces(Class c) {
15     ObjectRenderer r = (ObjectRenderer) map.get(c);
16      if (r !=  null) {
17          return r;
18     }  else {
19         Class[] ia = c.getInterfaces();
20          for ( int i = 0; i < ia.length; i++) {
21             r = searchInterfaces(ia[i]);
22              if (r !=  null)
23                  return r;
24         }
25     }
26      return  null;
27 }
28  public ObjectRenderer getDefaultRenderer() {
29      return defaultRenderer;
30 }
31  public  void put(Class clazz, ObjectRenderer or) {
32     map.put(clazz, or);
33 }

ThrowableRendererSupport接口

ThrowableRendererSupport接口用于支持设置和获取ThrowableRenderer,从而用户能够自定义对Throwable对象的渲染。

1  public  interface ThrowableRendererSupport {
2     ThrowableRenderer getThrowableRenderer();
3      void setThrowableRenderer(ThrowableRenderer renderer);
4 }

Hierarchy类以属性的方式实现了该接口,于是每一个Hierarchy实例只能有一个全局的ThrowableRenderer,而不能像ObjectRender那样为不一样的类定义不一样的render。当时这种设计也是合理的,由于对Throwable的渲染最主要的就是其栈的渲染,其余的没什么大的不一样,并且对栈渲染方式保持相同的格式会比较好。

 1  private ThrowableRenderer throwableRenderer =  null;
 2  public Hierarchy(Logger root) {
 3     
 4     defaultFactory =  new DefaultCategoryFactory();
 5     
 6 }
 7  public  void setThrowableRenderer( final ThrowableRenderer renderer) {
 8     throwableRenderer = renderer;
 9 }
10  public ThrowableRenderer getThrowableRenderer() {
11      return throwableRenderer;
12 }

Configurator接口

Configurator接口用于定义对配置文件的解析。在Log4J中配置文件解析出来的全部信息均可以放在LoggerRepository中,于是Configurator接口的定义很是简单。

1  public  interface Configurator {
2      public  static  final String INHERITED = "inherited";
3      public  static  final String NULL = "null";
4      void doConfigure(URL url, LoggerRepository repository);
5 }

Log4J支持两种文件形式的配置文件:properties文件和xml文件,他们风别对应PropertyConfigurator类和DOMConfigurator类。

PropertyConfigurator

PropertyConfigurator类解析properties文件的中的配置信息,能够设置log4j.debugtrue以打开Log4J内部的日志信息;另外PropertyConfigurator还支持Linux风格的变量,即全部${variable}形式的变量都会被系统中对应的属性或配置文件内部定义的属性替换(先查找系统中的属性,后查找配置文件内部定义的属性);可是PropertyConfigurator不支持一些Log4J中的高级功能,如自定义ErrorHandler和定义AsyncAppender等。

Configurator中最重要的方法是doConfigure()方法,在PropertyConfigurator实现中,首先将配置文件对应的URL读取成Properties对象:

 1  public  void doConfigure(java.net.URL configURL, LoggerRepository hierarchy) {
 2     Properties props =  new Properties();
 3     
 4         uConn = configURL.openConnection();
 5         uConn.setUseCaches( false);
 6         istream = uConn.getInputStream();
 7         props.load(istream);
 8     
 9     doConfigure(props, hierarchy);
10 }

然后检查是否设置了log4j.debuglog4j.resetlog4j.threshold等属性,若是有则作相应的设置。这里经过OptionConverter.findAndSubst()方法实现属性的查找和变量信息的替换。

 1  public  void doConfigure(Properties properties, LoggerRepository hierarchy) {
 2     repository = hierarchy;
 3     String value = properties.getProperty(LogLog.DEBUG_KEY);
 4      if (value ==  null) {
 5         value = properties.getProperty("log4j.configDebug");
 6          if (value !=  null)
 7             LogLog.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead.");
 8     }
 9      if (value !=  null) {
10         LogLog.setInternalDebugging(OptionConverter.toBoolean(value,  true));
11     }
12     String reset = properties.getProperty(RESET_KEY);
13      if (reset !=  null && OptionConverter.toBoolean(reset,  false)) {
14         hierarchy.resetConfiguration();
15     }
16     String thresholdStr = OptionConverter.findAndSubst(THRESHOLD_PREFIX,
17             properties);
18      if (thresholdStr !=  null) {
19         hierarchy.setThreshold(OptionConverter.toLevel(thresholdStr,
20                 (Level) Level.ALL));
21         LogLog.debug("Hierarchy threshold set to ["
22                 + hierarchy.getThreshold() + "].");
23     }
24     
25 }

而后分三步解析配置信息:

1.       解析Root Logger配置

首先找到log4j.rootLogger的值,它以逗号’,’分隔,其中第一个值时rootLevel信息,以后是要添加到rootAppender名字。对Level信息,直接设置给root就行。对Appender名字,继续解析。

 1  void parseCategory(Properties props, Logger logger, String optionKey,
 2         String loggerName, String value) {
 3     LogLog.debug("Parsing for [" + loggerName + "] with value=[" + value
 4             + "].");
 5     StringTokenizer st =  new StringTokenizer(value, ",");
 6      if (!(value.startsWith(",") || value.equals(""))) {
 7          if (!st.hasMoreTokens())
 8              return;
 9         String levelStr = st.nextToken();
10         LogLog.debug("Level token is [" + levelStr + "].");
11          if (INHERITED.equalsIgnoreCase(levelStr)
12                 || NULL.equalsIgnoreCase(levelStr)) {
13              if (loggerName.equals(INTERNAL_ROOT_NAME)) {
14                 LogLog.warn("The root logger cannot be set to null.");
15             }  else {
16                 logger.setLevel( null);
17             }
18         }  else {
19             logger.setLevel(OptionConverter.toLevel(levelStr,
20                     (Level) Level.DEBUG));
21         }
22         LogLog.debug("Category " + loggerName + " set to "
23                 + logger.getLevel());
24     }
25     logger.removeAllAppenders();
26     Appender appender;
27     String appenderName;
28      while (st.hasMoreTokens()) {
29         appenderName = st.nextToken().trim();
30          if (appenderName ==  null || appenderName.equals(","))
31              continue;
32         LogLog.debug("Parsing appender named \"" + appenderName + "\".");
33         appender = parseAppender(props, appenderName);
34          if (appender !=  null) {
35             logger.addAppender(appender);
36         }
37     }
38 }

相同的Appender能够添加到不一样的Logger中,于是PropertyConfiguratorAppender作了缓存,若是能从缓存中找到相应的Appender类,则直接返回找到的Appender

然后解析如下键值名以及对应类的属性信息:

log4j.appender.appenderName=…

log4j.appender.appenderName.layout=…

log4j.appender.appenderName.errorhandler=…

log4j.appender.appenderName.filter.filterKey.name=…

 1 Appender parseAppender(Properties props, String appenderName) {
 2     Appender appender = registryGet(appenderName);
 3      if ((appender !=  null)) {
 4         LogLog.debug("Appender \"" + appenderName
 5                 + "\" was already parsed.");
 6          return appender;
 7     }
 8     String prefix = APPENDER_PREFIX + appenderName;
 9     String layoutPrefix = prefix + ".layout";
10     appender = (Appender) OptionConverter.instantiateByKey(props, prefix,
11             org.apache.log4j.Appender. classnull);
12      if (appender ==  null) {
13         LogLog.error("Could not instantiate appender named \""
14                 + appenderName + "\".");
15          return  null;
16     }
17     appender.setName(appenderName);
18      if (appender  instanceof OptionHandler) {
19          if (appender.requiresLayout()) {
20             Layout layout = (Layout) OptionConverter.instantiateByKey(
21                     props, layoutPrefix, Layout. classnull);
22              if (layout !=  null) {
23                 appender.setLayout(layout);
24                 LogLog.debug("Parsing layout options for \"" + appenderName
25                         + "\".");
26                 PropertySetter.setProperties(layout, props, layoutPrefix
27                         + ".");
28                 LogLog.debug("End of parsing for \"" + appenderName + "\".");
29             }
30         }
31          final String errorHandlerPrefix = prefix + ".errorhandler";
32         String errorHandlerClass = OptionConverter.findAndSubst(
33                 errorHandlerPrefix, props);
34          if (errorHandlerClass !=  null) {
35             ErrorHandler eh = (ErrorHandler) OptionConverter
36                     .instantiateByKey(props, errorHandlerPrefix,
37                             ErrorHandler. classnull);
38              if (eh !=  null) {
39                 appender.setErrorHandler(eh);
40                 LogLog.debug("Parsing errorhandler options for \""
41                         + appenderName + "\".");
42                 parseErrorHandler(eh, errorHandlerPrefix, props, repository);
43                  final Properties edited =  new Properties();
44                  final String[] keys =  new String[] {
45                         errorHandlerPrefix + "." + ROOT_REF,
46                         errorHandlerPrefix + "." + LOGGER_REF,
47                         errorHandlerPrefix + "." + APPENDER_REF_TAG };
48                  for (Iterator iter = props.entrySet().iterator(); iter
49                         .hasNext();) {
50                     Map.Entry entry = (Map.Entry) iter.next();
51                      int i = 0;
52                      for (; i < keys.length; i++) {
53                          if (keys[i].equals(entry.getKey()))
54                              break;
55                     }
56                      if (i == keys.length) {
57                         edited.put(entry.getKey(), entry.getValue());
58                     }
59                 }
60                 PropertySetter.setProperties(eh, edited, errorHandlerPrefix
61                         + ".");
62                 LogLog.debug("End of errorhandler parsing for \""
63                         + appenderName + "\".");
64             }
65         }
66         PropertySetter.setProperties(appender, props, prefix + ".");
67         LogLog.debug("Parsed \"" + appenderName + "\" options.");
68     }
69     parseAppenderFilters(props, appenderName, appender);
70     registryPut(appender);
71      return appender;
72 }

2.       解析LoggerFactory配置

查找log4j.loggerFactory的值,保存建立的LoggerFactory实例,使用log4j.loggerFactory.propName的方式设置LoggerFactory实例的属性。

 1  protected  void configureLoggerFactory(Properties props) {
 2     String factoryClassName = OptionConverter.findAndSubst(
 3             LOGGER_FACTORY_KEY, props);
 4      if (factoryClassName !=  null) {
 5         LogLog.debug("Setting category factory to [" + factoryClassName
 6                 + "].");
 7         loggerFactory = (LoggerFactory) OptionConverter
 8                 .instantiateByClassName(factoryClassName,
 9                         LoggerFactory. class, loggerFactory);
10         PropertySetter.setProperties(loggerFactory, props, FACTORY_PREFIX
11                 + ".");
12     }
13 }

3.       解析非Root LoggerObjectRender配置

解析log4j.logger.log4j.renderer.log4j.throwableRenderer.等信息。

另外,PropertyConfigurator还经过PropertyWatchLog类支持每一个一段时间检查一次,若是发现配置文件有改动,则自动从新加载配置信息。

DOMConfigurator

DOMConfigurator使用DOM解析全部Log4J配置文件中的元素,并根据DOM中的元素查找对应的RootLoggerLoggerAppenderLayout等模块。另外DOMConfigurator也支持每隔一段时间检查文件是否有修改,如有,则从新载入新修改后的配置文件。这里DOMConfigurator的实现方式和PropertyConfigurator的实现方式相似,再也不详细介绍。

LogManager

LogManagerConfiguratorLoggerRepository整合在一块儿,它在初始化的时候找到Log4J配置文件,而且将其解析到LoggerRepository中。

 1  static  {
 2      Hierarchy h  =   new  Hierarchy( new  RootLogger((Level) Level.DEBUG));
 3      repositorySelector  =   new  DefaultRepositorySelector(h);
 4      String override  =  OptionConverter.getSystemProperty(
 5              DEFAULT_INIT_OVERRIDE_KEY,  null );
 6       if  (override  ==   null   ||   " false " .equalsIgnoreCase(override)) {
 7          String configurationOptionStr  =  OptionConverter.getSystemProperty(
 8                  DEFAULT_CONFIGURATION_KEY,  null );
 9          String configuratorClassName  =  OptionConverter.getSystemProperty(
10                  CONFIGURATOR_CLASS_KEY,  null );
11          URL url  =   null ;
12           if  (configurationOptionStr  ==   null ) {
13              url  =  Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
14               if  (url  ==   null ) {
15                  url  =  Loader.getResource(DEFAULT_CONFIGURATION_FILE);
16              }
17          }  else  {
18               try  {
19                  url  =   new  URL(configurationOptionStr);
20              }  catch  (MalformedURLException ex) {
21                  url  =  Loader.getResource(configurationOptionStr);
22              }
23          }
24           if  (url  !=   null ) {
25              LogLog.debug( " Using URL [ "   +  url
26                       +   " ] for automatic log4j configuration. " );
27               try  {
28                  OptionConverter.selectAndConfigure(url,
29                          configuratorClassName,
30                          LogManager.getLoggerRepository());
31              }  catch  (NoClassDefFoundError e) {
32                  LogLog.warn( " Error during default initialization " , e);
33              }
34          }  else  {
35              LogLog.debug( " Could not find resource: [ "
36                       +  configurationOptionStr  +   " ]. " );
37          }
38      }  else  {
39          LogLog.debug( " Default initialization of overridden by  "
40                   +  DEFAULT_INIT_OVERRIDE_KEY  +   " property. " );
41      }
42  }
相关文章
相关标签/搜索