一个 日志系统 是让您把信息写到一个或多个目的的机制。 一个 Logger 有以下组件:
您应把日志系统用于以下2个主要目的:
在本节中,您将学习如何使用 java.util.logging 包提供的类来添加日志系统到并发应用。
本节的示例代码在 com.elanzone.books.noteeg.chpt8.sect06 package中
MyFormatter 类 : extends java.util.logging.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(); }
MyLogger 类
private static Handler handler;
public static Logger getLogger(String name) { // 5. 获得与 name 关联的 Logger Logger logger = Logger.getLogger(name); // 6. 设置日志级别以输出所有级别的日志 logger.setLevel(Level.ALL); try { // 在用到时才创建 handler if (handler == null) { handler = new FileHandler("recipe8.log"); Formatter format = new MyFormatter(); handler.setFormatter(format); } if (logger.getHandlers().length == 0) { logger.addHandler(handler); } } catch (IOException e) { e.printStackTrace(); } return logger; }
Runnable 实现类 : Task : implements Runnable
@Override public void run() { // 12. 获得类名对应的 Logger Logger logger = MyLogger.getLogger(this.getClass().getName()); // 13. 用 entering() 方法输出一条表示方法开始执行的日志信息 logger.entering(Thread.currentThread().getName(), "run()"); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } // 14. 用 exiting() 方法输出一条表示方法结束执行的日志信息 logger.exiting(Thread.currentThread().getName(), "run()", Thread.currentThread()); }
控制类 : Main
public static void main(String[] args) { Logger logger = MyLogger.getLogger("Core"); // 17. 用 entering() 方法输出一条表示主程序开始执行的日志信息 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(); } // 20. 输出 INFO 级别的日志,表示已创建了线程 logger.log(Level.INFO, "Ten Threads created. Waiting for its finalization"); // 21. 调用 join() 方法等待5个线程结束。每个线程计数后,输出日志表示线程已结束 for (Thread thread : threads) { try { thread.join(); logger.log(Level.INFO, "Thread has finished its execution", thread); } catch (InterruptedException e) { logger.log(Level.SEVERE, "Exception", e); } } // 22. 用 exiting() 方法输出一条表示主程序结束执行的日志信息 logger.exiting("Core", "main()"); }
在本节中,使用了Java logging API 提供的 Logger 类来在一个并发应用中写日志信息。 首先,实现了 MyFormatter 来来给日志信息一个格式。此类扩展了声明了抽象方法 format() 的 Formatter 类。 format() 方法接受一个有日志消息的所有信息的 LogRecord 对象作为参数,并返回一个格式好的日志消息。 在 MyFormatter 类中,使用了 LogRecord 类的以下方法来获得日志消息的信息:
MyLogger 类实现了静态方法 getLogger(), 此方法创建一个 Logger 对象并设置一个 Handler 对象以把应用的日志消息按 MyFormatter 格式写到 recipe8.log 文件。 使用了 Logger 类的静态方法 getLogger() 来创建 Logger 对象。此方法对作为参数传递的每个名字返回一个不同的对象。 只创建了一个 Handler 对象,所以所有 Logger 对象将把日志写入同一个文件。 还配置了此 logger 写所有级别的日志消息,不管消息的级别。
最后,实现了一个 Task 对象和一个主程序,它们写不同的日志信息到日志文件。用了以下方法:
使用日志系统时,必须考虑2个重点:
有其他的类库提供比 java.util.logging 包更完备的日志系统,例如 Log4j 或 slf4j 库。 但 java.util.logging 包是 Java API 的一部分,并且所有方法是 多线程安全 的,所以能用之于并发应用,没有问题。