考虑以下情况:
(1).根据程序输出可以很好地帮助程序员调试程序。在编写小型程序时,可以在程序中不断增加 System.out.print() 语句来查看程序运行状态;在大型系统中,显然这一做法非常不对,可以考虑将调试信息通过日志处理工具,输出到一个文本文件中,然后查看文件内容。
(2).在系统开始线上运行后,系统管理人员需要获知系统的运行情况,包括但不限于系统开始运行时间、系统关闭时间、系统目前处理任务等,这些状态需要输出到一个经过格式化的日志文件中,供管理人员查看;
(3).在编写系统,甚至是系统运行过程中,都可能会发生错误,导致系统崩溃——先不论系统崩溃本身能不能接受,系统崩溃后找不到崩溃原因是绝对不能接受的。因此需要日志管理工具将系统发生严重错误时的状态信息,比如发生错误时正在执行什么任务,处理什么数据,放入日志中。
(4).........
以上情况,涉及两个需求:#1. 记录信息;#2. 分类记录信息。好吧,其实是一个需求,我们可以把它当作两个。
记录信息就是将信息字符串输出到一个输出流,该输出流可能流向的目的地,不管是长久存放的文件,更长久存放的数据库还是临时查看的控制台,是需要程序员指定的;而对信息的分类工作,则是在输出时,使用关键字 info(普通信息)、debug(调试信息)、 error(错误)以及 fatal(严重错误)等进行区分。进行区分的另一个好处是,可以通过配置使系统只输出error信息,自动屏蔽其它信息,这样遍布程序各处的调试输出语句就不用一句一句地删除了。
JDK自带的 java.util.logging 日志工具包,第三方 log4j 及其它等都是按照这个思路来处理日志,只是在帮助程序员更方便的处理日志的程度上有所不同,以及其它一些细微区分。
1. JDK自带的java.util.logging包。
java.util.logging为JDK标准库中的日志处理包,JDK1.4及之后版本可用。JDK自带的工具包,当然在易用性上有目共睹……在区分日志输出信息时,它也是标杆性地严谨,见下表
级别 |
SEVERE |
WARNING |
INFO |
CONFIG |
FINE |
FINER |
FINEST |
调用方法 |
severe() |
warning() |
info() |
config() |
fine() |
finer() |
finest() |
含意 |
严重 |
警告 |
信息 |
配置 |
良好 |
较好 |
最好 |
不同级别的输出,调用不同的方法就可以。级别之间也设定了不同的优先级,从左到右优先级依次递减,在配置输出类别时,指定一个关键字,由该关键字以及优先级高于该关键字代表的信息将会被输出到日志输出流,优先级低的会被自动屏蔽。比如,设置关键字为 WARNING, 那么只有severe()方法与warning()方法的输出会被写入输出流,info()等会被屏蔽。
如上提到,在使用日志处理工具时,要根据需要配置内容,比如应该将哪几个级别的信息输出、输出目的地是文件还是控制台,甚至输出信息的格式是纯文本还是XML格式文本,输出中的时间应该是24小时还是12小时表示……理所当然地,要在一个配置文件中指定以上内容。
先看例子(运行一遍试试):
import java.util.logging.Logger; public class logTest { static Logger logger = Logger.getLogger(logTest.class.getName()); public static double division(int value1, int value2) { double result = 0; try { result = value1 / value2; } catch(ArithmeticException e) { logger.warning("divide by 0. Error"); e.printStackTrace(); } return result; } public static void main(String[] args) { System.out.println(division(5, 0)); } }
在这个简单的例子中,首先获取一个 java.util.logging.Logger 对象 logger,指定logger的名字为 类名,也可以是其它字符串;然后在需要输出日志的地方,只需要使用logger对象调用相应的方法就可以了,比如logger.info(),logger.warning()。这个例子使用了默认的配置,日志输出流目的地是控制台,日志输出级别是 info。
接下来的例子是使用自定义的配置文件,配置日志输出流目的地以及日志输出级别。
先看源代码,如下
import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; import java.util.logging.Logger; import java.util.logging.FileHandler; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.LogRecord; import java.util.logging.SimpleFormatter; public class logTest{ static Logger logger = Logger.getLogger(logTest.class.getName()); static LogManager logManager = LogManager.getLogManager(); static{ InputStream inputStream = null; FileInputStream fis = null; try{ fis = new FileInputStream("log.properties"); logManager.readConfiguration(fis); logManager.addLogger(logger); }catch(IOException e){ e.printStackTrace(); }finally{ fis.close(); } } public static double division(int value1, int value2){ double result = 0; try{ result = value1/value2; }catch(ArithmeticException ex){ logger.severe("divided by 0 error"); logger.warning("divided by 0 error"); logger.info("divided by 0 error"); logger.config("divided by 0 error"); logger.fine("divided by 0 error"); logger.finer("divided by 0 error"); logger.finest("divided by 0 error"); System.out.println(ex.getMessage()); } return result; } public static void main(String[] args){ System.out.println(division(5,0)); } }
这段代码中,LogManager 通过调用 readConfiguration() 方法将当前目录下的 log.properties 配置文件读取,并通过 addLogger() 方法设置当前Logger实例 logger 的配置。使用日志处理工具时,依然是简单的 logger.info() 方式的调用。
接下来看 log.properties 文件中的配置,也可以参考JDK默认的配置文件,位于 %JAVA_HOME%jreliblogging.properties 。
#"handlers" specifies a comma separated list of log Handler #handlers= java.util.logging.ConsoleHandler handlers= java.util.logging.FileHandler, # Default logging level. .level= CONFIG # default file output is in "D:project_javajavalog" directory. java.util.logging.FileHandler.pattern = D:/project_java/java/log/Log%g.xml java.util.logging.FileHandler.limit = 50000 java.util.logging.FileHandler.count = 3 java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter # Limit the message that are printed on the console to CONFIG and above. java.util.logging.ConsoleHandler.level = CONFIG java.util.logging.ConsoleHandler.formatter = java.util.logging.XMLFormatter
handlers 指定 输出到控制台还是文件,使用完整类名指定,控制台为 java.util.logging.ConsoleHandler,文件为 java.util.logging.FileHandler
如果handlers指定了文件,指定 java.util.logging.FileHandler.Pattern的值,确定日志文件的输出地址以及命名格式。格式中 %g 的取值意义参见JDK。.level=CONFIG 表示日志输出级别为CONFIG,没有前缀时,表示不论是控制台,还是文件,输出级别都采用CONFIG, 单独指定可以采用 java.util.logging.ConsoleHandler=INFO。
2. log4j API
有了JDK自带的日志处理工具,为什么还要用 log4j API?这篇文章很详细,总结说,就是 log4j 比JDK自带API提供了更多功能,这些功能使用JDK自带的API也容易实现,但是如果已经有了现成的工具包,为啥还要重新发明雨刷器呢——毕竟占用精力需要重新发明的东西,得跟轮子一样重要吧!
事实上,JDK自带API是参考了log4j的实现。很多概念都很相同,除了在定义输出级别上,并不如前者那么严谨,参见下表:
FATAL(严重的 )、ERROR(错误 )、WARN(警告)、INFO(信息)、DEBUG(调试 )。
FATAL | ERROR | WARN | INFO | DEBUG |
严重错误 | 错误 | 警告 | 信息 | 调试 |
log4j的配置文件,可以是properties文件,也可以是XML文件,以properties文件为例进行介绍,先看源代码,如下。
import org.apache.log4j.Logger; import org.apache.log4j.ConsoleAppender; public class testLog { /** * @param args */ private static Logger logger=Logger.getLogger(testLog.class); public static void main(String[] args) { System.out.println("this is testLog111222"); logger.debug("this is debug message1112222"); logger.info("this is info message"); logger.error("this is error message"); } }
log4j 读取配置文件进行输出,所需的代码只有一行,得到 org.apache.log4j.Logger 类的实例 logger,指定 logger 名称,并调用 logger.debug() 方法。Logger.getLog() 在执行过程中,会自动加载 properties 文件。
properties 文件如下。
log4j.rootLogger=DEBUG,testlog log4j.appender.testlog=org.apache.log4j.FileAppender #log4j.appender.testlog.layout=org.apache.log4j.PatternLayout log4j.appender.testlog.file=testLog.log log4j.appender.testlog.layout=org.apache.log4j.TTCCLayout
参考文章地址 http://blog.csdn.net/luoweifu/article/details/46495045