log4j使用、源码简析与怎么出炉呢

    本文根据log4j-1.2.17,先介绍开发J2EE中使用,后面是深入源码分析,主要是如何Head first来弄这个log。

    Apache的log4j是最常用的java日志处理工具,通常用起来非常容易,看到不少人还喜欢用System.out.println(),强烈建议不要用了。有人说jsp里怎么办?下面介绍。

一、简单使用
    通常很简单,写一个配置文件,把log4j-1.2.17.jar扔到lib里,你的类定义一个对象就可以了,日常在配置中设置下输出模块与级别就行了。
1.写一个log4j.properties放在WEB-INF/classes下面,文件内容简单如下:
  # 根日志的级别与输出去向,这句有两个去向。
  log4j.rootLogger=WARN, stdout, R
  # 去向stdout的配置情况,这里是去控制台。
  log4j.appender.stdout=org.apache.log4j.ConsoleAppender
  log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
  log4j.appender.stdout.layout.ConversionPattern=%d [%t] %-5p %c - %m%n
  # 去向R的配置情况,这里是记录在一个文件中。
  log4j.appender.R=org.apache.log4j.RollingFileAppender
  log4j.appender.R.File=example.log
  log4j.appender.R.MaxFileSize=100KB
  log4j.appender.R.MaxBackupIndex=1
  log4j.appender.R.layout=org.apache.log4j.PatternLayout
  log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
  # 我要输出的地方与级别,一般开发中只调整这里。很简单吧~
  log4j.logger.我的模块1包=INFO
    log4j.logger.我的模块2包=DEBUG


    由于包时原LogManager的静态初始化方法中会加载属性,所以不需要人工做什么,只要写好这个配置文件。

2.在类中的使用
    在你的每一个需要日志的类中,import org.apache.log4j.Logger后,在类写一句属性对象:
   
private static final Logger log= Logger.getLogger({你的类名}.class);


    后面在需要日志的地方,用log.debut...或者  log.info...或者log.error等输出日志就行了。从名字看出,在调试时,都用debug,正式运行时要输出的用info,出错用error。以后在上面的配置文件中就可以控制输出级别了。正式运行时你用info级别,所有的debug日志就不会出现了,干净很多。

3.在jsp中使用
     一般来说不需要jsp中写,特别是使用各种前台框架的项目。但我们的项目中,jsp中也会写不少后台代码,所以免不了用日志,很多人就用System.out.println了,而我认为还是用log4j。网上也没找到合适的,自己调试一下,这样写:
   
final Logger log= Logger.getLogger("jsp.项目名.文件夹1级.文件夹2级.页面名_jsp");

     加了static不可以,一定要有final,我只记得匿名内部类使用一个在其外部定的对象,那么编译器会要求其参数引用是final的。不知道内部处理jsp到class时,是不是有这方面的原因呢?不去深究了。
     Log的名字,与页面在文件夹中的层次关系一样,中间用.分隔开,最后的文件后缀的.换成了_,因为包中用.区分层次。实际上log内部关系也是用.来区分的,所以这样处理一下。配置文件中可以用类似这样的语句控制文件夹1级下的页面的输出级别:
log4j.logger.{jsp.项目名.文件夹1级}=INFO



二、log4j源码是什么样?
1.主要的两个对象
    居然不用Hashmap而用Hashtable?不用ArrayList而用vector?好老啊。简单看了一下,主要是Category.java与Hierarchy.java两个类,Category实际上就是具体的日志类,Hierarchy是个容器,里面主要是用hashtable来放日志类。
    Hierarchy作为日志对象,属性有:名字,级别,父级日志对象,国际化资源,指向日志的容器,输出的动向表,是否附加父级的去向。
    看到了吧,每个日志对象都可以分开配置自己独特的级别,去向等内容。再看一下,日志对象与父级对象都有啥关系?


    整个项目中找到4个与父亲有关的地方,分别是要父级的:去向,国际化资源,级别。所以一个子日志对象,可以继承父日志对象的很多内容。

    那么,什么是父级对象呢?实际上就是按日志的名字来区分的,重点就是包分隔用的.来区分。

2.处理父子日志对象的巧妙之处
    root日志对象是所有日志对象的根对象。用getLogger("a.b.c.d.e.f")举例吧。如果你只用到f这个类,那f类的日志对象名字就是"a.b.c.d.e.f",它的父类日志对象应该是"a","a.b", "a.b.c", "a.b.c.d", "a.b.c.d.e" 这么几个,但父类日志对象目前不存在啊,那就建这些节点的虚拟对象,有点象空文件夹,每级的日志虚拟对象里存着实际上存在的这个日志对象,那f的日志对象目前的直接父类是root日志。
    而以后如果中间某个日志对象有了,也许它的名字与子包名字一毛一样,这样就要替换掉那个虚拟对象,再把虚拟对象中临时存放的f日志对象的父亲,换成新出现的这个日志对象,而新出现的这个的父级将变成f原来的父对象,那就是root对象了。
    这样才能实现在配置中,日志级别按包名字级别,来控制子对象,甚至自定义每个级别的去向,级别了。这个很少用到,但看了源码就知道非常的灵活了。
    为什么配置文件可以那么简单?上面分析可以看出,通常只配置root的级别与去向就行了。子日志对象就直接继承下来了,实际用的时候,我们最多改动就是输出那个位置的日志与输出的级别,所以通常只改一下想输出的部分,一句话设置一个模块就够了。


3.其它几个对象
    另外去向是一个appender对象,常用的有控制台去向,日志文件去向,包括每天产生一个的DailyRollingFileAppender,还有一个什么JDBCAppender, JMSAppender, SocketAppender 等等,后面这些以前没听到过。
    还有什么Layout的对象,看名字就知道是布局格式之类的,比如:SimpleLayout, xmlLayer, HTMLLayer...,上面的配置中有:layout=org.apache.log4j.PatternLayout。
    其它对象都不太重要了,不深看了。

三、分析完源码,分析一下如何设计日志
    看完源码,感觉太完善了太灵活了。除了用Hashtable,用vector太老之外,现在嘛,不考虑并发用Hashmap,并发用concurrentHashmap。vector加锁效率太低,不过CopyOnWriteArrayList 使用也受限。
    假设一下:比如老板说,目前的日志太乱了,但当时log4j还没出生,怎么办?也许此法可以开拓思路,尝试经历一下那个log4j开发人员所经历的思考,提升我们未来处理问题的能力。
1. 专门的人做专门的事
    面向对象的语言,我喜欢从现实生活中对比。社会中,如果通用的功能,会找一个专职人员。比如会议签到,每个与会人员去那里登记一下。如果开始到处自己写syso,那么第一步,我会设计一个类,专门处理各个类产生的日志,甚至是一个静态类。谁要处理,把信息传给我就行了,当然都要引用到这个静态类。由我统一syso。

2.部署时,要擦去好多syso
    开发时写了很多,实际部署不要那么多日志,但有时候找问题又要,那必须有一个级别来控制,那专职日志对象要求传过来日志级别。

3.不能总看控制台,还要有文件
    那就需要配置一下了,把日志同时写到文件中,可以控制文件大小,也可以控制每天一个,也可以控制只生成重要的日志。

4.有的包要输出,有的包不需要
    老板的想法又进一步,想想是合理的,每人做一块,只想看自己的DEBUG日志,其它的不想看,这就比前面那些麻烦多了。如同考核在不在岗位,有的部门一直在的,有的是需要出去跑业务的。好的,那搞一个名单,每个考核人登记时告诉名字,查找部门,如果在要考核部门,那就登记。这个名单???怎么弄,怎么起名字??好吧,对象的全名字不就行了,配置时弄个包的部分前缀,可以用indexOf来判断。

5.按类的命名起名字不错,能不能再灵活,不同级别都灵活定义?
    这个真正复杂起来了,每个包都配置不一样?一个统一的日志类貌似不合理了,要分裂出很多的日志类了。一个统一的日志员,通过查名单表不行了,每人一个移动日志员。每一个日志类对象都有自己不同的属性,记录自己的配置。
    这时候可能还是用indexOf来判断,如果某层与下层,与下下层,与下下...层都不一样,那indexOf实在是太乱了,也许这时候想到日志对象串起来,只用.来串效率太差了,但串起来是依据.来的。把.的级别关系换成父子引用的关系,运行起来更快。缺失的节点补成特别节点,有点象文件夹与文件的关系了。
    其它的什么去向,格式啊都与特定的日志对象有关,当然可以从父级拿到,如果没有就是从root中拿,所以配置文件中必须有一个root的级别与去向的配置。

6.如果通常使用的情况下,这个日志是不是复杂了?
    如果你的类层次很多,一般只按级别与模块输出,那产生的日志对象(包括虚拟对象)貌似有点多。所以如果根据实际情况(我们项目几乎都很用的简单配置),去掉一些几乎不用的灵活性,是不是可以只用一个日志root对象,加上点简单的判断,固定上几乎不变的输出去向内容,做一个非常小的日志工具呢?软件毕竟不同于现实中的低配版,高配版。

猜你喜欢

转载自herman-liu76.iteye.com/blog/2368172
今日推荐