读书人

log4j学习札记之初始化

发布时间: 2012-10-26 10:30:59 作者: rapoo

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设置此appenderif (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类型的链表中

读书人网 >编程

热点推荐