使用log
System.out.println() 有很大的不足之处, 当程序调试结束后, 将这个程序投入产品化时, 往往需要消除 System.out.println()的输出, 这时候可能需要逐行扫描, 麻烦.
log4j 是一个功能强大, 简单易用的日志工具, 因此 log4j 除了可用于辅助调试之外, 还可用于记录程序的运行日志. log4j 已经可以支持 c, c++, c#, perl, python 等等.
1. 下载和安装 log4j
http://logging.apache.org/log4j/ 下载后解压, 看到如下文件结构:
- examples: 该文件夹下包含了 log4j 的一些使用范例.
- site: 该文件夹下包含了 log4j 官方站点的相关页面, 包括 log4j的API文档
- src: 该文件夹下包含了log4j 的源代码.
- tests: 该文件夹下包含了 log4j 的一些测试用例.
- log4j-1.2.15.jar: 这个JAR包就是log4j项目的核心工具包.
- 其他一些杂项文件.
最简单的做法是将 log4j-1.2.15.jar 添加到系统的 CLASSPATH环境变量中, 以后即可在程序中使用 log4j日志工具包. 或者将log4j-1.2.15.jar核心类库添加到应用的类的加载路径里, 如WEB应用, 将 log4j-1.2.15.jar添加到WEB-INF/lib路径下即可.
2. Logger
Logger 是 log4j 最重要一个组件, 它代表一个日志输出器, 其功能有点类似 System.out的功能, 也就是说, 在程序中通过 Logger即可进行输出, Logger比 System.out更强大的地方在于, 它可以自由的关闭部分一些调试信息的输出, 而不影响其他人得调用.
为了让 Logger 能关闭部分调试信息的输出而不影响其他人, Logger必须对日志进行分类控制, log4j 里 Logger 的分类是通过它的名字来控制的,Logger 的名字是一个区分大小写的, 形如 org.crazyit.empsys.Manager 的字符串.
在程序中得到一个Logger非常简单, Logger提供了如下两个静态方法来获得 Logger对象.
- static Logger getLogger(java.lang.Class clazz): 以clazz类的类名作为 Logger的名字创建Logger, 该方法的本质是 getLogger(clazz.getName())
- static Logger getLogger(java.lang.String name): 创建一个名为 name 的 Logger 对象.
从上面介绍不难看出, Logger 的名字与 Java 包名的集成关系非常相似, 这一点是 log4j 故意设计的. 通过这种方式控制 Logger 之间的继承关系, 例如 名为 org 的 Logger 是名为 org.crazyit的祖先
log4j 还提供了一个根(root) Logger, 它是所有 Logger 的祖先, 根具有两个特征:
- 根 Logger 总是存在的
- 程序不能通过使用字符串名字得到根, 只能通过Logger的静态方法 getRootLogger 来得到根.
得到对象之后, 可以通过 5 个方法执行输出
- public void debug(Object message, [Throwable t]): 以 debug 级别输出 message 信息, 如果传入了 t 参数, 还会输出 t 的跟踪栈信息.
- public void info(Object message, [Throwable t]):
- public void warn(Object message, [Throwable t]):
- public void error(Object message, [Throwable t]):
- public void fatal(Object message, [Throwable t]):
从上面可以看出, Logger 执行输出时, 涉及到一个级别问题. log4j 为上面 5 个方法提供了 5 个对应的级别, 它们的优先级从低到高依次为: DEBUG<INFO<WARN<ERROR<FATAL
与此同时, log4j准许调用Logger的setLevel(Level level) 方法设置自身的级别, 例如: logger.setLevel(level.DEBUG);
log4j 制定了一个规则: 只有当Logger的输出方法的级别高于或等于 Logger 本身的级别时, 这条输出语句才会生成真正的输出. 示例如下:
Logger logger = Logger.getLogger("com.foo");
logger.setLevel(Level.INFO); // 设置 Logger 本身的优先级别 INFO
logger.warn("www.crazyit.org"); // 这条输出语句生成输出, 因为 WARN > INFO
logger.debug("www.cryzit.org"); // 这条输出语句不会输出, 因为 DEBUG < INFO
使用 Logger 输出调试信息只要3隔步骤,
1) 调用 Logger 的 getLogger() 静态方法返回一个 Logger对象
2) 使用 setLevel()方法设置 Logger的级别
3) 使用 Logger 的 debug(), info(), warn(), error(), fatal() 方法输出信息.
如果程序每次都需要通过3个步骤来执行输出, 那 log4j 只比 System.out 稍微灵活一点, 当程序需要关闭某批调试输出时, 将它们对应的 Logger 的 level 设置的更高即可. 但是这样也不很好.
为此, log4j 又制定了一个规则: 如果程序没有为某个 Logger 显示指定其 level, 它将继承离它最近的祖先的 level.
程序无需为每个 Logger 显示设置 level, 如果想控制一批 Logger 的信息输出, 可以控制这批 Logger 的最顶层 Logger 的 level 即可.
为了便捷地控制每个类中得 Logger, 习惯上将每个 Logger的名称设为与当前类的类名相同. 例如: Logger log = Logger.getLogger(EmpManager.class);
假如 EmpManager 类位于 org.crazyit.salary.service 包下, 那这个 Logger 的名字就是 org.crazyit.salary.service.EmpManager.
3. Appender 和 Layout
log4j 可以将日志输出到各种设备, 例如控制台, 文件, GUI组件, 远程 Socket 服务器 等, 而且可以将日志输出到多个输出设备中.
log4j 使用 Appender 来代表日志信息的输出目的,
ConsoleAppender: 输出到控制台
DailyRollingFileAppender: 输出到文件的 Appender. 等等很多.
Logger 提供了一个 addAppender() 方法用于将指定 Appender 添加为与 Logger 关联. 默认情况下, 每个Logger关联的Appender是其所有祖先Logger关联的Logger和它自己关联的 Appender的总和.
Layout 是指输出的式样
HTMLLayout: 控制日志输出成 HTML 格式的 Layout.
XMLLayout, PatternLayout(字符串),SimpleLayout(简单日志信息)
4. 使用 log4j 输出
实际开发中使用 log4j 时几乎不会再 java 代码中定义 Logger, Appender 和 Layout, 而通常都回采用配置文件来管理, log4j 准许以下两种格式的配置文件.
Properties 属性文件格式的配置文件, 通常配置文件名为 log4j.properties
xml 格式的配置文件, 通常配置文件名为 log4j.xml
不管使用那种文件, 配置的方法都是一样的. 配置以下 3 个部分.
配置一个或多个 Logger, 这些 Logger 用于控制输出
还需要配置一个或多个 Appender, Appender 代表日志的输出设备, 配置 Appender 时需要制定使用的 Layout
将 Logger 和 一个或多个 Appender 进行关联.
例如: log4j.xml
<?xml version="1.0" encoding="gdk" ?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> <!-- 定义输出到控制台的 Appender --> <appender name="stdout" class="org.apache.log4j.ConsoleAppender"> <!-- 定义该 Appender 对应的 Layout --> <Layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="===CRAZYIT.ORG=== %r %d [%t] %-p %c %x - #%l# - %m%n"/> </layout> </appender> <!-- 该logger将对 lee 包及其所有子包之内的 Logger 起作用 --> <logger name="lee"> <!-- 输出 debug 及更高级别的日志 --> <level value=“debug”/> <!-- 将日志输出到控制台 --> <appender-ref ref="stdout"/> </logger> <!-- 制定日志级别, 引用那些 Appender --> <root> <!-- 输出 info 及其更高级别的日志 --> <level value="info"/> <appender-ref ref="stdout"/> </root> </log4j:configuration>
然后就可以在程序中直接使用 logger.debug("开始执行程序");
其他方法
1. 利用编译器的提示信息
2. 跟踪程序的执行流程
3. 断点调试 ( IDE 提供, 比如 eclipse )
4. 隔离调试 (将一部分代码注释掉)
5. 调试的基本思路( 分段调试 )