摘要
你的Java程序是否被很多随即放置的System.out.println语句和stack traces(例如:ex.printStackTrace())弄乱呢?当你加入调试信息到工程中的类中,你的信息输出是否穿插于其他开发者的很多信息中,使得您的信息很难辨认和读取呢?你是否使用简单的日志API,并担心它可能不能提供您需要的足够的弹性和能力,当您的程序已存在于产品中时呢?如果以上的问题的答案都是Yes,到了你该选择一个工业级和强大的日志API和使用它的时候了!
这篇文章将通过评估两种广为使用的Java日志库:the Apache Group's Log4j和java.util.logging包(使用JUL代替),来帮助您选择日志API。这个文章调查了每一个库如何实现日志,评价他们的区别和相似点,并提供一些简单的指导路线,可以帮助您决定选择哪一个库。
Log4j的介绍
Log4j是一个开源日志库,被开发为Apache Software Foundation's日志服务项目的一个子项目。基于IBM开发的日志库早在1990年,它的第一个版本出现在1999。Log4j是一个广为使用在开源社区,包括一些大型项目比如JBoss和Hibernate。
Log4j的架构建立于三个大的概念上面:loggers,appenders,和layouts.(日志子,追加器,布局器)。这些概念允许开发者记录信息根据他们的类型和优先级,和来控制信息在哪结束以及他们的形式。loggers是你的应用程序首先调用的对象,用来初始化信息的日志记录。当loggers被给与一条信息来记录,loggers生成了Logging-Event对象来进行封装给定信息。该loggers此时交接LoggingEvents给它们关联的appender。Appenders发送LoggingEvents包含的信息到指定的输出目的地。例如,一个ConsoleAppender将信息写到System.out或者一个FileAppender将它附加于一个文件中。在发送LoggingEvent信息到最后输出目的地之前,一些appender使用布局器来创建以预期格式格式化的对于信息的文本表达。例如,Log4j包括了XMLLayout类来用于格式化loggingEvents以XML的字符串。
在Log4j中,LoggingEvent被赋予一个级别来指定他们的优先级。在Log4j中的默认级别(从高到低):OFF,FATAL,ERROR,WARN,INFO,DEBUG,和ALL.
Loggers和appenders也被分配到一个级别,并且只有当logging请求的级别比他们的级别相等并且更大时,才会执行。例如,如果一个appender他的级别是ERROR,它被请求输出一个有着WARN的级别的LoggingEvent,appender将不会写出被给与的logEvent。
Log4j中的所有loggers有一个名字。Log4j组织logger实例到一个树型的结构中,根据他们名字,同Java语言中的包的组织一样。正如Log4j的文档简洁的表述:“一个logger被认为是另外的logger的祖先,如果它的名字紧跟着一个点是后代的这个logger名字的前缀。”例如,一个名为"org.nrdc"被认为是"org"logger的子代。"org.nrdc.logging"logger是"org.nrdc" logger的子代,并且是"org" logger的重子代。如果logger的没有明确的指定级别,它会使用已经赋予级别的最近的父辈的级别。Loggers继承appenders从他们的父类,尽管他们也能被配置只使用直接赋给他们的appender。
当一个logger被请求来记录信息,它首先检查请求的级别是否比它的有效级别相同或者更大。如果是,它就创建LoggingEvent根据给定的信息,并传送LoggingEvent到它的appenders,格式它,并发送它到输出目的地。
JUL的介绍
java.util.logging包,Sun在2002年 Java SDK 1.4中介绍了它,作为JSR 47到来的,Logging API 规范。JUL非常类似于Log4j - 它多多少少的正确的使用了同样的概念,但是对他们重新命名了。例如,appenders 叫 “handlers”,layout 叫 “formatters”,并且LoggingEvent叫做“LogRecords”。图片1,汇总了Log4j和JUL的名称和概念。JUL使用级别与Log4j使用级别是一样的,并且JUL日志继承属性properties从他们父loggers中,有点像Log4j继承属性从父层次中。从Log4j到JUL概念几乎是一对一的;虽然两个库在细节上有所不同,任何熟悉Log4j的开发者只需要调整他们的词库就可以理解JUL。
功能不同点
虽然Log4j和JUL几乎有同样的概念,他们在功能上不同。他们的可以归纳为,“无论JUL能不能做成什么,Log4j都能做 - 并且做得更多。”他们主要在几个领域不同,appender/handler的实现,有用的格式化器/布局实现器,和配置灵活性上。
JUL包含4种具体的handler的实现,而Log4j则包括超过12个的appender实现。JUL的handler足够用来进行基本的日志记录 - 他们允许你写入到一个buffer,一个console,一个socket,和一个file中。Log4j的appenders,另一方面,大概覆盖了所有logging输出目的地你可以想到的。他们可以写到NT日志或者Unix syslog中,或者甚至发送Email。图片2提供了JUL的handler和Log4j的appenders的汇总。
JUL包含了两个格式化类:XMLFormatter和SimpleFormatter。Log4j包含了对应的布局器:XMLLayout和SimpleLayout.Log4j还提供了TTCCLayout,它格式化LoggingEvents到富内容字符串,和HTMLLayout,它可格式化LoggingEvent到HMTL表格中。
TTCCLayout和HTMLLayout都很有用,Log4j的确领先于JUL,在formatter/handler方面,由于PatternLayout。PatternLayout实例能够采用一个通过字符转换式模式而带来具有大量有弹性的被配置,类似于在C中的printf函数的表达式类型。在PatternLayout转换式模式中,特定转化字符被用来指定在布局格式化输出的信息。例如,"%t"被用来指定开始记录信息的线程;"%C"被用于开始记录信息的对象的类的名称;并且"%m"指定了信息。"%t: %m"将导致输出类似于这样"main thread:This is my message." "%C - %t:%m"会导致输出类似于"org.nrdc.My-Class - main thread:This is my message." Pattern-Layout非常有用,然而JUL的两个formatter类没有任何地方来匹配这样的多功能性。对于JUL使用者,自行定制formatter类是很少见,反之大多数Log4j用户通常需要学会如何使用PatternLayout转换式模式。
Log4j和JUL同时都能够使用配置文件进行配置,Log4j允许更广范围内的配置的可能性相对于JUL使用的配置文件。JUL能够使用.properties文件配置,但是到了J2SE5.0之前,handlers的配置只能是对于每一个类的而不是对于每一个实例。这就意味着如果你将使用Tiger版本的SDK,你将遗漏有用的配置选择,例如可以设置不同的FileHandler实例发送他们的输出到不同的文件中。
非常重要的,注意Tiger版的JUL能够很容易的被配置为写到多个文件,通过编写代码,而不是通过默认的配置机制。Log4j能够通过.properties文件或者XML文件配置,并且appenders能够以每一个实例为基础进行配置。同时,Log4j允许开发者关联布局实例和appender实例,并以每一个实例为基础进行配置布局。这包括PatternLayout实例 - 你能够设置转换式每一个模式的使用,在配置文件中。在开发过程中,通过重新编译来调整日志配置不是问题,开发结束后,然而,你或许不想通过重新编译来改变或者完全重新配置应用中的日志。这时,Log4j提供了更多的弹性,尤其使用Tiger。
Log4j提供了很多JUL缺少的功能,虽然JUL正在赶上。JUL可以完全扩展后,来做Log4j做的事情 - 你可以写更多的handlers,重新实现PatternLayout来为JUL,并且更新JUL配置机制,这些都不是很难。但是要在Log4j已存在这些特色数年时,为什么还做这些事呢?
你要选择哪一个库呢?
类似于这样的重要决定经常使得leaders失眠或者过早衰老。幸运的是,这个决定可以通过检测对于简单的问题后,容易的做出。
问题1
你过早需要那些JUL没有而Log4j有的handlers,例如SMTPHandler,NTEventLogHandler,或者其他任何非常便利的FileHandlers么?
问题2
你是否想频繁的切换你日志输出的格式?你是否需要简单的方式这样做?另外,你是否需要Log4j的PatternLayout?
问题3
你是否明确的需要在您的应用中有这种能力,来改变复杂的日志配置在您的应用中,即使他们已经被编译了并且部署到产品环境了?你的配置是不是听起来像:“来自这个类的几条信息通过邮件发送给技术支持工作者;类子集的几条信息被记录于我们服务器的syslog中;类子集的警告信息以一个文件记录于网络磁盘A中;并且各处所有信息以文件记录于网络磁盘B”?你是否没过10几天,就会改变一下?
如果你对上面的问题都回答Yes,使用Log4j。如果你的回答全是明确的No,JUL足够了,它已存在SDK中了。
结论
Log4j和JUL是非常相似的API。他们只在一些细节上不同,最后做的是同样的事情,除了Log4j有了更多的特色,但也许你用不到。
紧记,当你移植到你选择的日志库,那么日志将影响应用的性能。使得影响最小化的方法,就是尽量的重用loggers的引用;保存静态或者引用指针到loggers,而不是每次你需要一个logger时,调用Logger.getLogger("loggerName")。log表达式放置于公有区域,而不是循环中。
这篇文章不是深入研究如何使用Log4j或者JUL的教程,并且,实际上,演示了两个库很多的有用的特点,例如MBeans支持(J2SE5.0,你能够设置JUL记录级别远程的通过JMX),和ResourceBundle支持。还有很多Log4j的高级特点,例如filter链和Object-Renderers。因特网有很多如何使用他们的教程,JDJ中也有;在编码之前,一定找到并学习他们。
资源
Aggarwal, V."Third Party Logging API." Java Developer's Journal, Vol. 5, issue 11: http://sys-con.com/story/?storyid=36144
Banes, J. "Building the Ultimate Logging Solution." Java Developer's Journal, Vol. 9, issue 5: http://sys-con.com/story/?storyid=44698
Writing a sweet Log4j Appender that sends instant messages: www.106.ibm.com/developerworks/java/library/j-instlog/
For those who don't like JUL or Log4j, try the Logging Toolkit for Java from IBM: www.alphaworks.ibm.com/tech/loggingtoolkit4j
posted on 2005-10-27 12:37 北国狼人的BloG 阅读(3999) 评论(1) 编辑 收藏 所属分类: 翻译Java文章