log4j学习札记之初始化

log4j学习笔记之初始化
最近学了一下log4j,作个标记:
log4j的配置文件有两种格式:xml和properties,分别由类DOMConfigurator和PropertyConfigurator进行处理,在此假设log4j配置文件是xml格式的,看看DOMConfigurator的configure方法
static public void configure(String filename) throws FactoryConfigurationError {
    new DOMConfigurator().doConfigure(filename,LogManager.getLoggerRepository());
}

通过RepositorySelector获得LoggerRepository,LoggerRepository是logger仓库,可以从它里面得到logger,每一个logger实例都有一个LoggerRepository引用,LoggerRepository是一个接口,其实现者是Hierarahy.初始化方法在doConfigure中完成。
public void doConfigure(final URL url, LoggerRepository repository) {
      ParseAction action = new ParseAction() {
          public Document parse(final DocumentBuilder parser) throws SAXException,               IOException {
              URLConnection uConn = url.openConnection();
              uConn.setUseCaches(false);
              InputSource src = new InputSource(uConn.getInputStream());
              src.setSystemId(url.toString());
              return parser.parse(src);
          }
          public String toString() { 
              return "url [" + url.toString() + "]"; 
          }
      };
      doConfigure(action, repository);
  }

首先创建了ParseAction接口的一个实例,然后执行重载的doConfigure(action, repository)方法,ParseAction中只有parse方法,用于解析DocumentBuilder对象。
private final void doConfigure(final ParseAction action, final LoggerRepository repository) throws FactoryConfigurationError {
      ...
      //通过DocumentBuilderFactory取得DocumentBuilder实例docBuilder
      ...
      //ParseAction将docBuilder解决成Document 
      Document doc = action.parse(docBuilder);     
      parse(doc.getDocumentElement());
       ...
 }

protected void parse(Element element) {
    //取得根元素,判断合理性
    //repository设置一些属性
    //获取子元素,做两次遍历,第一次处理categoryFactory和loggerFactory
    for (int loop = 0; loop < length; loop++) {
      currentNode = children.item(loop);
      if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
	currentElement = (Element) currentNode;
	tagName = currentElement.getTagName();
	if (tagName.equals(CATEGORY_FACTORY_TAG) || tagName.equals(LOGGER_FACTORY_TAG)) {
	  parseCategoryFactory(currentElement);
	}
      }
    }
    
    //第二次处理logger和root等
    for (int loop = 0; loop < length; loop++) {
      currentNode = children.item(loop);
      if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
	currentElement = (Element) currentNode;
	tagName = currentElement.getTagName();
	if (tagName.equals(CATEGORY) || tagName.equals(LOGGER)) {
          //处理logger元素
	  parseCategory(currentElement);
	} else if (tagName.equals(ROOT_TAG)) {
          //处理root元素
	  parseRoot(currentElement);
	} 
       ......
      }
  }



protected void parseCategory (Element loggerElement) {
    ...
    String className = subst(loggerElement.getAttribute(CLASS_ATTR));
    if(EMPTY_STR.equals(className)) {
      LogLog.debug("Retreiving an instance of org.apache.log4j.Logger.");
	  //1、如果class没有设置,从repository中获取logger
      cat = (catFactory == null) ? repository.getLogger(catName) : repository.getLogger(catName, catFactory);
    }
    else {
      LogLog.debug("Desired logger sub-class: ["+className+']');
       try {	 
	 Class clazz = Loader.loadClass(className);
	 Method getInstanceMethod = clazz.getMethod("getLogger", 
						    ONE_STRING_PARAM);
	 cat = (Logger) getInstanceMethod.invoke(null, new Object[] {catName});
       } catch (InvocationTargetException oops) {
          if (oops.getTargetException() instanceof InterruptedException
                  || oops.getTargetException() instanceof InterruptedIOException) {
              Thread.currentThread().interrupt();
          }
          LogLog.error("Could not retrieve category ["+catName+
		      "]. Reported error follows.", oops);
	      return;
       } catch (Exception oops) {
	      LogLog.error("Could not retrieve category ["+catName+
		      "]. Reported error follows.", oops);
	      return;
       }
    }
    synchronized(cat) {
      boolean additivity = OptionConverter.toBoolean(
                           subst(loggerElement.getAttribute(ADDITIVITY_ATTR)),
			   true);
      LogLog.debug("Setting ["+cat.getName()+"] additivity to ["+additivity+"].");
      cat.setAdditivity(additivity);
	  //2、解析子元素
      parseChildrenOfLoggerElement(loggerElement, cat, false);
    }
  }



1、看一下当class未设置时,是如何取到logger的
public Logger getLogger(String name, LoggerFactory factory) {
...
    synchronized(ht) {
      Object o = ht.get(key);
	  //如果为null,则创建一个logger,并放入hashtable中,更新parent logger
      if(o == null) {
	logger = factory.makeNewLoggerInstance(name);
	logger.setHierarchy(this);
	ht.put(key, logger);
	updateParents(logger);
	return logger;
      } 
	  //如果有,则直接返回
	  else if(o instanceof Logger) {
	return (Logger) o;
      } 
	  //有,但是ProvisionNode实例,要同时更新parent logger和children logger
	  else if (o instanceof ProvisionNode) {
	logger = factory.makeNewLoggerInstance(name);
	logger.setHierarchy(this);
	ht.put(key, logger);
	updateChildren((ProvisionNode) o, logger);
	updateParents(logger);
	return logger;
      }
    ...
    }
  }



ProvisionNode继承至Vector,并提供了一个接受一个Logger的构造方法。在Log4j的层次结构中仅仅是一个站位符,是虚拟节点,并不是一个logger。
ProvisionNode保存所有子Logger的实例。下面看一下updateParents和updateChildren两个方法。
final
  private
  void updateParents(Logger cat) {
    String name = cat.name;
    int length = name.length();
    boolean parentFound = false;

    // 尝试取得祖先Logger的实例,可能存在如下情况
  //1)、如果父Logger存在,且类型为Logger,则设置为本Logger的父Logger。结束循环。
  //2)、如果父Logger存在,且类型为ProvisionNode,则将本Logger添加到ProvisionNode中,然后继续循环。
  //3)、如果父Logger不存在,就创建相应的ProvisionNode,则将本Logger添加到ProvisionNode中,然后继续循环。
    //2,3中之所以要添加logger到ProvisionNode中,是为了让ProvisionNode中保存所有子Logger的实例,便于后续遍历

    for(int i = name.lastIndexOf('.', length-1); i >= 0;
	                                 i = name.lastIndexOf('.', i-1))  {
      String substr = name.substring(0, i);

      //System.out.println("Updating parent : " + substr);
      CategoryKey key = new CategoryKey(substr); // simple constructor
      Object o = ht.get(key);
      // Create a provision node for a future parent.
      if(o == null) {
	//System.out.println("No parent "+substr+" found. Creating ProvisionNode.");
	ProvisionNode pn = new ProvisionNode(cat);
	ht.put(key, pn);
      } else if(o instanceof Category) {
	parentFound = true;
	cat.parent = (Category) o;
	//System.out.println("Linking " + cat.name + " -> " + ((Category) o).name);
	break; // no need to update the ancestors of the closest ancestor
      } else if(o instanceof ProvisionNode) {
	((ProvisionNode) o).addElement(cat);
      }
     ...
  }


final private void updateChildren(ProvisionNode pn, Logger logger) {
   
    final int last = pn.size();
 //遍历ProvisionNode中所有的Logger,将不是以logger.name开始的logger设置成其parent logger,在log4j中,x.y是x.y.z的parent logger
    for(int i = 0; i < last; i++) {
      Logger l = (Logger) pn.elementAt(i);
      if(!l.parent.name.startsWith(logger.name)) {
	logger.parent = l.parent;
	l.parent = logger;
      }
    }
  }

2、解析子元素
protected void parseChildrenOfLoggerElement(Element catElement, Logger cat, boolean isRoot) {
    
    //设置子元素,比如level,additive等
    PropertySetter propSetter = new PropertySetter(cat);
    //清空appenders
    cat.removeAllAppenders();
    NodeList children 	= catElement.getChildNodes();
    final int length 	= children.getLength();
    
    for (int loop = 0; loop < length; loop++) {
      Node currentNode = children.item(loop);

      if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
	Element currentElement = (Element) currentNode;
	String tagName = currentElement.getTagName();
	
	//如果有设置appender-ref,则为该logger设置此appender
	if (tagName.equals(APPENDER_REF_TAG)) {
	  Element appenderRef = (Element) currentNode;
	  Appender appender = findAppenderByReference(appenderRef);
	  String refName =  subst(appenderRef.getAttribute(REF_ATTR));
	  if(appender != null)
	    LogLog.debug("Adding appender named ["+ refName+ 
			 "] to category ["+cat.getName()+"].");
	  else 
	    LogLog.debug("Appender named ["+ refName + "] not found.");
	    
	  cat.addAppender(appender);
	  
	} else if(tagName.equals(LEVEL_TAG)) {
	  parseLevel(currentElement, cat, isRoot);	
	} else if(tagName.equals(PRIORITY_TAG)) {
	  parseLevel(currentElement, cat, isRoot);
	} else if(tagName.equals(PARAM_TAG)) {
          setParameter(currentElement, propSetter);
	} else {
        quietParseUnrecognizedElement(cat, currentElement, props);
    }
      }
    }
    propSetter.activate();
  }

  protected Appender findAppenderByReference(Element appenderRef) {    
    String appenderName = subst(appenderRef.getAttribute(REF_ATTR));    
    Document doc = appenderRef.getOwnerDocument();
    return findAppenderByName(doc, appenderName);
  }

 protected Appender findAppenderByName(Document doc, String appenderName)  {      
    Appender appender = (Appender) appenderBag.get(appenderName);
    if(appender != null) {
      return appender;
    } else {
      Element element = null;
      NodeList list = doc.getElementsByTagName("appender");
      for (int t=0; t < list.getLength(); t++) {
	Node node = list.item(t);
	NamedNodeMap map= node.getAttributes();
	Node attrNode = map.getNamedItem("name");
	if (appenderName.equals(attrNode.getNodeValue())) {
	  element = (Element) node;
	  break;
	}
      }
      if(element == null) {
	LogLog.error("No appender named ["+appenderName+"] could be found."); 
	return null;
      } else {
          //调用parseAppender对appender子元素进行解析
	      appender = parseAppender(element);
          if (appender != null) {
            appenderBag.put(appenderName, appender);
          }
    return appender;
      }
    } 
  }

 protected Appender parseAppender (Element appenderElement) {
   ...
      PropertySetter propSetter = new PropertySetter(appender);
      ...
	   for (int loop = 0; loop < length; loop++) {
	if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
	  Element currentElement = (Element)currentNode;
	  // Parse appender parameters 
	  if (currentElement.getTagName().equals(PARAM_TAG)) {
            setParameter(currentElement, propSetter);
	  }
	  // 解析layout
	  else if (currentElement.getTagName().equals(LAYOUT_TAG)) {
	    appender.setLayout(parseLayout(currentElement));
	  }
	}
	...
      propSetter.activate();
      return appender;
    }
  ...
  }

 protected Layout parseLayout (Element layout_element) {
   ...
   //利用反射取得layout的实例,调用了默认构造方法,假设为PatternLayout
      Object instance 	= Loader.loadClass(className).newInstance();
      Layout layout   	= (Layout)instance;
    ......
  }

public final static String DEFAULT_CONVERSION_PATTERN ="%m%n";
private PatternConverter head;

 public PatternLayout() {
    this(DEFAULT_CONVERSION_PATTERN);
  }

  public PatternLayout(String pattern) {
    this.pattern = pattern;
	//将ConversionPattern解析为一个PatternConverter类型的链表
	//PatternConverter中的next引用下一个ConversionPattern
    head = createPatternParser((pattern == null) ? DEFAULT_CONVERSION_PATTERN : pattern).parse();
  }

总结:DOMConfigurator读取xml文件,从log4j:configuration的子元素遍历,分别处理root和logger,并遍历root和logger的子元素,设置一些属性,如果有appender-ref,则为这个logger查找appender并设置,并顺着这个appender解析其中的param,ConversionPattern等,param被设置到PropertySetter中,ConversionPattern被解析到一个名为head的ConversionPattern类型的链表中