概述
本文根据作者的开发经验叙述了对Java程序调试和日志的认识和理解。介绍了System.out.println, System.out.printf,手工制作的小工具类SysUtils.log,log4j和logback。
引言
在Java开发中,除了IDE提供的调试工具之外,监控代码中变量变化,跟踪代码运行有很多种方法:
1、招之即来的System.out.println
System.out.println无疑是最方便的,受众也是最广,知名度最高。Java学习者往往从第一个HelloWorld的例子开始就接触它,学习Java的人应该没有人不知道它。
示例代码:
System.out.println(sql);
System.out.println("My name is " + name + ", I am " + age + " years old.");
优点是显而易见的,缺点也不少,如:
只能输出到控制台;
大量的代码混在业务逻辑中,在生产环境中需要处理这些影响性能的代码。
字符串拼接容易带来性能损失(当然可以使用StringBuffer,但又稍显繁琐)
2、Java5对System.out.println的改进:System.out.printf
转向Java的C程序员无比怀念C语言中的printf,Java5也对此提供了支持,Java也开始为开发者着想了,看代码:
System.out.println(sql);
System.out.printf("My name is %S, I am %d years old.", name, age);
C程序员对此最熟悉不过了,无论是可读性和效率都比上面的好很多,唯一的缺点就是需要Java 5 的支持。
3、土制的utils小工具
System.out.println,带来的最大问题是从开发环境向生产环境转换时带的性能问题(当然其它的问题也不少),部署时应该去除或注释这些代码,。
解决这个问题最好的办法是土制一个小工具类,look:
public class SysUtils {
public static final void log(Object o) {
System.out.println("==" + String.valueOf(o));
}
}
以后的调试代码全部调用上面的静态方法,代码如下:
SysUtils.log(sql);
SysUtils.log("My name is " + name + ", I am " + age + " years old.");
部署的时候,只要把SysUtils.log里的System.out.println 注释掉就万事大吉了。
4、进阶过程,日志小知识
然而一切似乎还是没有改变,功能太弱,和代码耦合性太强...于是日志(log)出现了。日志,源于log,有航海日志的意思。指记录海员记录每天的行程,生活及发生的事件。在软件开发领域,用来监控代码中变量变化,跟踪代码运行的轨迹,在开发环境中担当调试器作用,向控制台或文件输出信息,运行环境中记录程序的警告和错误信息。
Java开源牛人们为了改变现状,不断开发出各种各样的日志工具,于是Java程序员受苦了,不得不在各种日志间转换奔波。
然而天下大事,分久必合,合久必分。于是乎commons-logging出现了。它提供了日志统一的接口和一个最简单的实现。Java程序员幸福了,因为只需要针对接口编程。而不管到底由谁来实现。
最著名的几个实现有:
Simplelog:最简单的实现。
Jul:java.util.logging,JDK中自带的日志实现
log4j:Apache Software Foundation开发的非常强大的日志实现
log主要有几个个概念:
输出级别:是调试信息,信息,还是警告,错误,致命错误等
输出目的地:输出到控制台,文件,可写设备,还是数据库
输出格式和内容:输出哪些东西,如何排版等。
5、log4J
Log4J的意思是Log for Java,4是for的简写,当然也读作 “for”
Log4J的输出级别:
Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。
Log4J的输出目的地:
ConsoleAppender(控制台)
FileAppender(文件)
DailyRollingFileAppender(每天产生一个日志文件)
RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
WriterAppender(将日志信息以流格式发送到任意指定的地方)
甚至能输出到数据库
Log4J的输出格式:
HTMLLayout(以HTML表格形式布局)
PatternLayout(可以灵活地指定布局模式)
SimpleLayout(包含日志信息的级别和信息字符串)
TTCCLayout(包含日志产生的时间、线程、类别等等信息)
Log4J的输出内容:
Log级别
类名
线程名
时间
位置等等。
强大到足以在开发环境或生产环境做一切你能想到的记录。
Log4J在Java Web项目中的使用方法:
1. commons-logging.jar, log4.jar扔到lib目录
2. log4j.properties文件扔到classes根目录
3. OK了。
示例代码:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class TestLog {
Log log = LogFactory.getLog(TestLog.class);
public void print() {
if (log.isDebugEnabled()) {
log.debug (sql);
log.debug ("My name is " + name + ", I am " + age + " years old.");
}
}
}
6、slf4j和logback,没有最好,只有更好
log4j经过这么久的发展,从外面看,经典,庄重,强壮,从内部看,却充满了代码的坏味道。就连它的作者也认为应该有一个更好的日志框架。于是,再次操刀,创建了slf4j来取代jcl,创建了logback来取代log4j。
目前,log4j和jul应用最为广泛,slf4j作为新兴的抽象层,整合logback,以其简洁,快速,正被越来越多的顶级项目使用。如大名鼎鼎的hibernate,Jetty。
十个转移到logback的理由(http://logback.qos.ch/10reasons.ppt)
slf4j支持参数化的logger.error("帐号ID:{}不存在", userId);告别了if(logger.isDebugEnable()) 时代。
另外logback的整体性能比log4j也较佳,hibernate等项目已经采用了slf4j:
"某些关键操作,比如判定是否记录一条日志语句的操作,其性能得到了显著的提高。这个操作在LOGBack中需要3纳秒,而在Log4J中则需要30纳 秒。 LOGBack创建记录器(logger)的速度也更快:13毫秒,而在Log4J中需要23毫秒。更重要的是,它获取已存在的记录器只需94纳秒,而 Log4J需要2234纳秒,时间减少到了1/23。"
Slf4j 相当于commons-logging 提供了一系列的日志接口
Logback相当于 log4j 提供了强大的实现
作者也称Logback是可靠,通用,快速,灵活的java日志工具(官方描述)。
commons-logging 和slf4j的代码比较:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestLogBySlf4J {
Logger logger = LoggerFactory.getLogger(TestLogBySlf4J.class);
public void print() {
logger.debug(sql);
logger.debug("My name is {}, I am {} years old.", name, age);
}
}
依稀又有了一点Java5的影子。
项目选择的思考。
如果是简单的项目,如小工具,小Demo等,采用System.out.printf , 土制工类,jul,都是不错的选择。
如果是web开发,宜采用log4j,因为系统已经有了很多的配置文件,不在乎多一个,而且功能强大。公司目前很多项目采用log4j。
如果是较新的项目,可采用slf4j,在学习成本不高的情况下,获得更好的架构,灵活性和性能。
小结
限于篇幅,本文只简要介绍了从原始的内置类到简单的封装,再到Log4j,再到强大的logback的进阶过程和主要示例代码,中间细节请查阅官方详细的文档。
参考资料
Slf4j官方网站(http://www.slf4j.org/)。
Logback官方网站(http://logback.qos.ch/)。
Jcl官方网站(http://commons.apache.org/logging/)。
Log4j官方网站(http://logging.apache.org/)。
SpringSide 官方wiki(http://wiki.springside.org.cn/)。
Java日志系统研究(http://yanboy.javaeye.com/blog/204436)
关于作者
kimsoft