`
wubo.wb
  • 浏览: 28536 次
  • 来自: 成都
文章分类
社区版块
存档分类
最新评论

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类型的链表中
分享到:
评论

相关推荐

    *Log4j 学习笔记

    配置 Log4j 可以通过默认的初始化过程或使用配置文件完成。默认情况下,Log4j 会尝试自动配置。但是,推荐使用 XML 配置文件,以便更精细地控制日志行为。XML 配置文件可以指定多个 Appender 和 Layout,以及它们与...

    Log4Net学习笔记

    ### Log4Net学习笔记 #### 一、简介与配置 **Log4Net** 是一个用于.NET平台的日志记录工具库,它提供了灵活且强大的日志记录功能,支持多种日志级别、格式化输出以及文件滚动等功能。对于刚接触Log4Net的新手来说...

    Archive Log 学习笔记 --oracle 数据库

    - 归档日志文件的位置:可以在数据库初始化参数文件(init.ora)中设置`LOG_ARCHIVE_DEST`参数来指定归档日志的存放位置。 - 归档日志策略:可以通过`DB_FILE_MULTIUSE`参数控制重做日志是否重复使用,以及`MAX_...

    pmon学习笔记

    从这些内容可以整理出,pmon学习笔记详细分析了引导程序从上电后至操作系统加载过程中的关键步骤,包括代码的存放、地址映射、硬件初始化、以及在系统启动过程中如何逐步将硬件设备配置成可用状态,为后续操作系统的...

    Debian linux学习笔记

    ### Debian Linux 学习笔记 #### 一、基本命令与目录结构 Debian Linux 是一个广泛使用的 Linux 发行版,其稳定性和安全性受到广大用户的认可。对于初学者来说,掌握 Debian Linux 的基本命令和目录结构至关重要。...

    ORACLE经典学习笔记

    ### ORACLE经典学习笔记知识点概览 #### 第一章 ORACLE 命令 本章节主要介绍了Oracle数据库中常用的命令及其使用方法。 1. **查看参数文件**: `Desc v$parameter` - 这个命令用于查看Oracle的参数文件信息,通过...

    Ora12c学习笔记.docx

    Oracle 12c 学习笔记 本文档旨在为读者提供 Oracle 12c 的基础知识,涵盖 Oracle 的基本介绍、存储的介绍、基本数据对象、表空间的管理、升级到 Oracle 12c 以及数据库的物理布局和存储管理等方面。 Oracle 体系...

    linux学习笔记--很经典

    本篇“Linux学习笔记”详细介绍了Linux的基本目录结构及其各自的功能,这对于初学者理解Linux系统的核心概念非常重要。 #### 二、Linux目录结构解析 1. **/(根目录)** - 根目录是Linux文件系统的起点,所有的...

    gulp学习笔记

    本学习笔记将深入探讨Gulp的核心概念、安装与配置,以及常见插件的使用方法。 首先,Gulp的安装是通过npm(Node.js的包管理器)完成的。在命令行中,进入项目目录并执行`npm init`初始化package.json文件,然后运行...

    SpringBoot2学习笔记.zip

    3. 日志管理:使用Logback或Log4j2作为日志系统,配置多级别的日志输出。 4. 微服务架构:SpringBoot适合构建微服务,利用Spring Cloud组件实现服务发现、熔断、配置中心等功能。 五、扩展与进阶 1. Spring Boot ...

    JavaScript-学习笔记.pdf

    以上是JavaScript学习笔记中提到的一些核心知识点,通过对这些知识点的理解和熟练应用,可以为进一步学习和掌握JavaScript打下坚实的基础。在实际开发过程中,结合具体的项目需求,这些知识会得到更深入的拓展和应用...

    斯坦福大学机器学习笔记(中文版)

    ### 斯坦福大学机器学习笔记(中文版)——核心知识点概述 #### 一、机器学习简介 **1.1 什么是机器学习?** 机器学习是计算机科学的一个分支,它研究如何让计算机从数据中自动“学习”并改进其性能。这种学习过程不...

    birt报表开发学习手册&&birt学习笔记-开发中常见问题

    在报表查询中,首次查询速度较慢通常是由于初始化操作、缓存加载等原因造成的。可以通过预加载数据、优化查询逻辑等方式来提升首次查询的速度。 #### 十一、查询特定时间段服务的处理示例 在处理特定时间段内服务...

    OA项目学习笔记整合

    - 使用Log4j记录日志,便于后期维护和问题追踪。 - 分页处理的实现,提高用户体验。 2. **分页逻辑封装**: - 通过ThreadLocal模式传递分页参数,减少Action和Manager之间的通信负担。 - 定义ThreadLocal实例,...

    cs224n学习笔记

    ### cs224n学习笔记:Recurrent Neural Networks 和 Language Models #### 1. 语言模型简介 **语言模型**是一种统计模型,用于计算给定单词序列(即句子)的概率 \(P(w_1, \ldots, w_m)\)。尽管听起来简单,但在...

    linux学习笔记分享

    - **/boot**:存储系统启动所需文件,如内核(/boot/vmlinuz)和初始化RAM磁盘(/boot/initrd)。 - **/dev**:设备文件的所在目录,代表系统中的硬件设备。 - **/etc**:存放系统级的配置文件,如网络设置(/etc...

Global site tag (gtag.js) - Google Analytics