欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-13 14:36:33
...
    本文根据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作为日志对象,属性有:名字,级别,父级日志对象,国际化资源,指向日志的容器,输出的动向表,是否附加父级的去向。
    看到了吧,每个日志对象都可以分开配置自己独特的级别,去向等内容。再看一下,日志对象与父级对象都有啥关系?

log4j使用、源码简析与怎么出炉呢
            
    
    博客分类: 源码  
    整个项目中找到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。log4j使用、源码简析与怎么出炉呢
            
    
    博客分类: 源码  

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

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

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

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

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