输出高效的日志信息
日志系统(Log System)是将信息输出到一个或者多个目标上的一种机制。一个日志器(Logger)有下面几个组件。
- 一个或多个处理器(Handler):处理器决定目标和日志消息的格式。可以把日志消息输出到控制台上、写到文件中或保存到数据库中。
- 一个名称(Name):一般来说,类中的日志记录器的名称是基于它的包名和类名的。
- 一个级别(Level):日志消息有一个关联的级别来表示它的重要性。日志记录器也有一个级别用来决定它要输出什么级别的消息。日志记录器仅输出与它的级别相同重要或者更重要的消息。
使用日志系统有以下两个主要目的:
- 当捕获到异常时尽可能多地输出信息,这有助于定位并解决错误;
- 输出关于程序正在执行的类和方法的信息。
在本节,我们将学习如何使用java.util.logging包提供的类来将一个日志系统增加到并发应用程序中。
1. 创建一个名为MyFormatter的类,继承java.util.logging.Formatter类。然后,实现抽象format()方法。它以LogRecord对象为参数,返回一个带有日志消息的String对象。
import java.util.Date; import java.util.logging.Formatter; import java.util.logging.LogRecord; public class MyFormatter extends Formatter { @Override public String format(LogRecord record) { StringBuilder sb = new StringBuilder(); sb.append("["+record.getLevel()+"] - "); sb.append(new Date(record.getMillis())+" : "); sb.append(record.getSourceClassName()+"."+record.getSourceMethodName()+" : "); sb.append(record.getMessage()+"\n"); return sb.toString(); } }
2. 创建一个名为MyLogger的类。
import java.io.IOException; import java.util.logging.FileHandler; import java.util.logging.Formatter; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; public class MyLogger { private static Handler handler; public static Logger getLogger(String name){ Logger logger = Logger.getLogger(name); logger.setLevel(Level.ALL); try { if(handler==null){ handler = new FileHandler("D:\\recipe8.log"); Formatter format = new MyFormatter(); handler.setFormatter(format); } if(logger.getHandlers().length==0){ logger.addHandler(handler); } } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return logger; } }
3. 创建一个名为Task的类,实现Runnable接口。它是用来测试Logger对象的任务。
import java.util.concurrent.TimeUnit; import java.util.logging.Logger; public class Task implements Runnable { @Override public void run() { try { Logger logger = MyLogger.getLogger(this.getClass().getName()); logger.entering(Thread.currentThread().getName(), "run()"); TimeUnit.SECONDS.sleep(2); logger.exiting(Thread.currentThread().getName(), "exit()", Thread.currentThread()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
4. 实现范例的主类Main,并实现main()方法。
import java.util.logging.Level; import java.util.logging.Logger; public class Main { public static void main(String[] args) { Logger logger = MyLogger.getLogger("Core"); logger.entering("Core", "main()",args); Thread threads[] = new Thread[5]; for(int i=0;i<threads.length;i++){ logger.log(Level.INFO, "Launching thread: ", +i); Task task = new Task(); threads[i] = new Thread(task); logger.log(Level.INFO, "Thread created: "+threads[i].getName()); threads[i].start(); } logger.log(Level.INFO, "Five Threads created. Waiting for its finalization."); try { for(int i=0;i<threads.length;i++){ threads[i].join(); logger.log(Level.INFO, "Thread has finished its execution", threads[i]); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } logger.exiting("Core", "main()"); } }
5. 控制台输出结果如下
十月 28, 2015 7:40:16 下午 Main main 信息: Launching thread: 十月 28, 2015 7:40:16 下午 Main main 信息: Thread created: Thread-1 十月 28, 2015 7:40:16 下午 Main main 信息: Launching thread: 十月 28, 2015 7:40:16 下午 Main main 信息: Thread created: Thread-2 十月 28, 2015 7:40:16 下午 Main main 信息: Launching thread: 十月 28, 2015 7:40:16 下午 Main main 信息: Thread created: Thread-3 十月 28, 2015 7:40:16 下午 Main main 信息: Launching thread: 十月 28, 2015 7:40:16 下午 Main main 信息: Thread created: Thread-4 十月 28, 2015 7:40:16 下午 Main main 信息: Launching thread: 十月 28, 2015 7:40:16 下午 Main main 信息: Thread created: Thread-5 十月 28, 2015 7:40:16 下午 Main main 信息: Five Threads created. Waiting for its finalization. 十月 28, 2015 7:40:18 下午 Main main 信息: Thread has finished its execution 十月 28, 2015 7:40:18 下午 Main main 信息: Thread has finished its execution 十月 28, 2015 7:40:18 下午 Main main 信息: Thread has finished its execution 十月 28, 2015 7:40:18 下午 Main main 信息: Thread has finished its execution 十月 28, 2015 7:40:18 下午 Main main 信息: Thread has finished its execution
6. 日志recipe8.log内容如下:
[FINER] - Wed Oct 28 19:40:16 CST 2015 : Core.main() : ENTRY [INFO] - Wed Oct 28 19:40:16 CST 2015 : Main.main : Launching thread: [INFO] - Wed Oct 28 19:40:16 CST 2015 : Main.main : Thread created: Thread-1 [INFO] - Wed Oct 28 19:40:16 CST 2015 : Main.main : Launching thread: [INFO] - Wed Oct 28 19:40:16 CST 2015 : Main.main : Thread created: Thread-2 [FINER] - Wed Oct 28 19:40:16 CST 2015 : Thread-1.run() : ENTRY [INFO] - Wed Oct 28 19:40:16 CST 2015 : Main.main : Launching thread: [INFO] - Wed Oct 28 19:40:16 CST 2015 : Main.main : Thread created: Thread-3 [INFO] - Wed Oct 28 19:40:16 CST 2015 : Main.main : Launching thread: [FINER] - Wed Oct 28 19:40:16 CST 2015 : Thread-3.run() : ENTRY [INFO] - Wed Oct 28 19:40:16 CST 2015 : Main.main : Thread created: Thread-4 [INFO] - Wed Oct 28 19:40:16 CST 2015 : Main.main : Launching thread: [FINER] - Wed Oct 28 19:40:16 CST 2015 : Thread-2.run() : ENTRY [FINER] - Wed Oct 28 19:40:16 CST 2015 : Thread-4.run() : ENTRY [INFO] - Wed Oct 28 19:40:16 CST 2015 : Main.main : Thread created: Thread-5 [INFO] - Wed Oct 28 19:40:16 CST 2015 : Main.main : Five Threads created. Waiting for its finalization. [FINER] - Wed Oct 28 19:40:16 CST 2015 : Thread-5.run() : ENTRY [FINER] - Wed Oct 28 19:40:18 CST 2015 : Thread-1.exit() : RETURN {0} [INFO] - Wed Oct 28 19:40:18 CST 2015 : Main.main : Thread has finished its execution [FINER] - Wed Oct 28 19:40:18 CST 2015 : Thread-3.exit() : RETURN {0} [FINER] - Wed Oct 28 19:40:18 CST 2015 : Thread-4.exit() : RETURN {0} [FINER] - Wed Oct 28 19:40:18 CST 2015 : Thread-2.exit() : RETURN {0} [INFO] - Wed Oct 28 19:40:18 CST 2015 : Main.main : Thread has finished its execution [INFO] - Wed Oct 28 19:40:18 CST 2015 : Main.main : Thread has finished its execution [INFO] - Wed Oct 28 19:40:18 CST 2015 : Main.main : Thread has finished its execution [FINER] - Wed Oct 28 19:40:18 CST 2015 : Thread-5.exit() : RETURN {0} [INFO] - Wed Oct 28 19:40:18 CST 2015 : Main.main : Thread has finished its execution [FINER] - Wed Oct 28 19:40:18 CST 2015 : Core.main() : RETURN
在上面的实现类中,已使用LogRecord类的下列方法来获取日志消息的信息。
- getLevel():返回消息级别。
- getMillis():返回发送消息到Logger对象时的时间(从1970开始计算的毫秒数)。
- getSourceClassName():返回发送消息到Logger的类的名称。
- getSourceMessageName():返回发送消息到Logger的方法的名称。
- getMessage()方法返回日志消息。
在主程序中,使用了下列方法。
- entering():输出FINER级别的消息表示方法开始执行。
- exiting():输出FINER级别的消息表示方法停止执行。
- log():输出带有指定级别的消息。
也有其他类库比java.util.logging包提供了更完全的日志系统,如Log4j或slf4j类库。但是java.util.logging包是Java API的一部分,并且它的所有方法都是安全的,所以用在并发应用程序中没有问题。