声明:本文是《 Java 7 Concurrency Cookbook 》的第八章, 作者: Javier Fernández González 译者:郑玉婷
写有效的日志
log工具提供了允许你写信息到一个或者多个目的地的机制。一个Logger是由以下这些组成:
- 一个或多个处理者: 一个处理者将决定目的地和log信息的格式。你可以把日志信息写入操控台,文档,或者数据库。
- 名字: 通常Logger使用的名字是基于类名或者它的包名。
- 等级: 日志信息有等级来表明它的重要性。Logger也有个等级是用来决定写什么样的信息。它只会写和这个等级一样重要的,或者更重要的信息。
为了以下2个主要目的,你应该使用log 系统:
- 当异常被捕捉,写尽可能多的信息。这个会帮助你定位错误并解决它。
- 写关于程序正在执行的类和方法的信息。
在这个指南,你将学习如何使用 java.util.logging 包提供的类来添加一个log系统到你的并发应用。
准备
指南中的例子是使用Eclipse IDE 来实现的。如果你使用Eclipse 或者其他的IDE,例如NetBeans, 打开并创建一个新的java项目。
怎么做呢…
按照这些步骤来实现下面的例子:
002 |
public class MyFormatter extends Formatter {
|
004 |
public String format(LogRecord record) {
|
006 |
StringBuilder sb= new StringBuilder();
|
007 |
sb.append( "[" +record.getLevel()+ "] - " );
|
008 |
sb.append( new Date(record.getMillis())+ " : " );
|
009 |
sb.append(record.getSourceClassName()+ "." +record.getSourceMethodName()+ " : " );
|
010 |
sb.append(record.getMessage()+ "\n" );
|
012 |
return sb.toString();
|
016 |
public class MyLogger {
|
019 |
private static Handler handler;
|
022 |
public static Logger getLogger(String name){
|
025 |
Logger logger=Logger.getLogger(name); |
028 |
logger.setLevel(Level.ALL); |
033 |
handler= new FileHandler( "recipe8.log" );
|
034 |
Formatter format= new MyFormatter();
|
035 |
handler.setFormatter(format);
|
039 |
if (logger.getHandlers().length== 0 ) {
|
040 |
logger.addHandler(handler);
|
042 |
} catch (SecurityException e) {
|
044 |
} catch (IOException e) {
|
053 |
public class Task implements Runnable {
|
060 |
Logger logger= MyLogger.getLogger( this .getClass().getName());
|
063 |
logger.entering(Thread.currentThread().getName(), "run()" );
|
066 |
TimeUnit.SECONDS.sleep( 2 );
|
067 |
} catch (InterruptedException e) {
|
072 |
logger.exiting(Thread.currentThread().getName(), "run()" ,Thread.currentThread());
|
078 |
public static void main(String[] args) {
|
081 |
Logger logger=MyLogger.getLogger( "Core" );
|
084 |
logger.entering( "Core" , "main()" ,args);
|
087 |
Thread threads[]= new Thread[ 5 ];
|
090 |
for ( int i= 0 ; i<threads.length; i++) {
|
091 |
logger.log(Level.INFO, "Launching thread: " +i);
|
092 |
Task task= new Task();
|
093 |
threads[i]= new Thread(task);
|
094 |
logger.log(Level.INFO, "Thread created: " + threads[i]. getName());
|
099 |
logger.log(Level.INFO, "Ten Threads created." + "Waiting for its finalization" );
|
102 |
for ( int i= 0 ; i<threads.length; i++) {
|
105 |
logger.log(Level.INFO, "Thread has finished its execution" ,threads[i]);
|
106 |
} catch (InterruptedException e) {
|
107 |
logger.log(Level.SEVERE, "Exception" , e);
|
112 |
logger.exiting( "Core" , "main()" );
|
它是如何工作的…
在这个指南里,你已经使用 Java logging API 提供的Logger类 在并发应用中写日志信息。首先,你实现了 MyFormatter 类来给日志信息一个格式。这个类扩展 Formatter 类,声明了抽象方法 format()。此方法接收 LogRecord 对象的全部日志消息信息,并返回一条格式化的日志信息。在你的类里使用了 LogRecord类的以下这些方法来获取日志信息:
- getLevel(): 返回的信息的级别。
- getMillis():返回日期,当一条信息被发送给 Logger 对象。
- getSourceClassName(): 返回发送信息给Logger的类的名字。
- getSourceMessageName():返回发送信息给Logger的方法的名字
- getMessage() 返回日志信息。MyLogger 类实现了静态方法 getLogger(), 创建 Logger 对象并分配 Handler 对象使用MyFormatter的格式在recipe8.log 文件中写入日志信息。你可以使用这个类的静态方法 getLogger() 创建对象。此方法返回每个不同的对象作为参数传递的名字。 你只要创建一个Handler对象,全部的Logger对象都会使用它在同一个文件中写日志信息。你还配置了logger写全部的日志信息,无论信息级别。
最后,你实现了 Task 对象和一个主程序在logfile写入不同的日志信息。你使用了以下的方法:
- entering():写 FINER 等级的信息,表明方法开始运行
- exiting(): 写 FINER 等级的信息,表明方法结束运行
- log(): 写特定级别的信息
更多…
当你使用log类时,你必须考虑2个重要点:
写必需的信息:如果你写过少的信息,那么logger就没有满足它的目的变的不是特别有作用。如果你写过多的信息,那么就会产生很大的日志文件,就不好管理且很难获取必要信息。
对信息使用适当的级别:如果你用高级别写入消息信息(information messages),或者使用低级别来写入报错信息,那么你就会让看logfiles的人很困惑。就会变得很难了解到底发生了什么错误,或者有过多的信息来分析错误的主要原因。
还有很多其他库比 java.util. logging 包提供了更完整的log系统,例如 Log4j 或者 slf4j libraries。但是 java.util.logging 是Java API 的一部分,而且它的全部方法都是 multi-thread safe,所以在并发应用中使用它将不会遇到任何问题。