log4j学习札记之初始化
log4j学习笔记之初始化
最近学了一下log4j,作个标记:
log4j的配置文件有两种格式:xml和properties,分别由类DOMConfigurator和PropertyConfigurator进行处理,在此假设log4j配置文件是xml格式的,看看DOMConfigurator的configure方法
通过RepositorySelector获得LoggerRepository,LoggerRepository是logger仓库,可以从它里面得到logger,每一个logger实例都有一个LoggerRepository引用,LoggerRepository是一个接口,其实现者是Hierarahy.初始化方法在doConfigure中完成。
首先创建了ParseAction接口的一个实例,然后执行重载的doConfigure(action, repository)方法,ParseAction中只有parse方法,用于解析DocumentBuilder对象。
1、看一下当class未设置时,是如何取到logger的
ProvisionNode继承至Vector,并提供了一个接受一个Logger的构造方法。在Log4j的层次结构中仅仅是一个站位符,是虚拟节点,并不是一个logger。
ProvisionNode保存所有子Logger的实例。下面看一下updateParents和updateChildren两个方法。
2、解析子元素
总结:DOMConfigurator读取xml文件,从log4j:configuration的子元素遍历,分别处理root和logger,并遍历root和logger的子元素,设置一些属性,如果有appender-ref,则为这个logger查找appender并设置,并顺着这个appender解析其中的param,ConversionPattern等,param被设置到PropertySetter中,ConversionPattern被解析到一个名为head的ConversionPattern类型的链表中
最近学了一下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类型的链表中