许多应用, 特别是企业应用, 都需要日志记录程序。应用日志有助于服务工程师解决这个领域的难题, 并且能为安全分析提供审计跟踪。
最初, Java 平台包含了很少可用于应用日志记录的资源。被迫面对日志文件中的println语句的开发人员和系统管理员,有的设法发明自家的解决方案,有的转而采用由商业开发商或者开放源码开发者生产的众多优秀(但互不兼容)的日志记录产品中的一种。Java 2版本1.4 改变了这种局面,因为它引入了java.util.logging包。
本期技巧简要地解释了Java日志记录的基本知识,并提供了示例代码,演示如何通过Java消息服务 (JMS)发送应用日志消息到某个独立客户端端。如果您不熟悉JMS, 参看2003年4月15 日那一期的“Publish/Subscribe Messaging With JMS Topics”以及 2003年3月11 日那一期的“使用JMS Queues(Using JMS Queues)”。
java.util.logging 包
Java logging包最初是作为Java Specification Request (JSR-047)创建的,现在是Java 2, Standard Edition (J2SE)的一部份。它的需求包括以下特性:
- 最小的运行时性能影响。
- 运行时启用/禁用日志记录。
- 细粒度控制。
- 运行时日志记录服务注册。
- 与现有日志记录服务的互操作性, 比如,系统日志和遗留日志记录方案。
- 在合适的时候向用户显示高优先级消息。
- 能够处理国际化的日志记录消息。
- 能够记录对象, 而不只是记录字符串 。
由于logging包是J2SE 的一部分, 它在其他Java 平台也是可用的, 包括J2EE。具体地,Java logging可以用于应用客户端、servlets 、JSP 页面、企业bean 和连接器。
Logging类和接口
日志记录程序的主要类是 Logger。 Logger表示一种通道,记录(logging)消息可以通过它进行发送。通常,每个类有它自己的Logger。
Logger 用Level来配置,这是一个指明问题严重性的类,用于指明所报告的问题的严重性和/或该类需要的详情级别。每一条发送到Logger的消息都有一个关联 Level。 Logger 只报告那些Level比该Logger高或者与之相同的错误。
最高的日志记录 Level 是 SEVERE, 表明碰到了一个严重问题—— 经常指的是一个致命错误。其他 Level 值(以递减顺序) 是 WARNING 和 INFO, 分别为可恢复的错误和信息类消息。 CONFIG 级别表明, 配置事件(比如正在读一个属性文件) 发生过。编程人员可以使用 的Level 值有: FINE、FINER 和 FINEST ,以连续报告更加细粒度的登录消息。服务工程师可以使用这些 Level值来隐藏外部细节, 或在类到类(class-by-class)的基础上增加日志记录细节的级别。Logger的缺省 Level 是 INFO。
LogRecord 是一个表示应该写入日志的一条消息的对象。它包含各种各样的信息, 包括被打印的消息, 最初发送给Logger的名字, Level 和所发送消息的创建时间与日期, 以及调用者的线程id。
一个叫做 Handler的抽象类表示一个知道如何以一种有用方式表示 LogRecord的类。logging包包含一些 Handler类 (比如 ConsoleHandler、FileHandler、StreamHandler 和 SocketHandler) ,它们报告 LogRecords 各种类型的目标。每一 Logger都可以有多 个Handler。 每个 Handler 还可以有一个 Level, 摒除任何 LogRecord ,其Level 在它自己之下。因此, 例如, 单个 Logger 可能有二个Handler: 一个用于在磁盘上写入日记文件,而另一个用于将日志消息发送到系统管理员的寻呼机。磁盘日志 Handler 可以用Level FINEST来配置,因此它将所有日志消息写入磁盘。相反, 寻呼机日志 Handler可能只报告 SEVERE 日志消息。
Formatter类(及其所定义的任何子类)将LogRecord转换为String以便打印。例如, Formatter 的XMLFormatter 子类将LogRecord 写为格式良好的XML。开发人员可以编写定制的 Formatter 类。
最后, Filter 接口允许编程人员编写方法(isLoggable),该方法允许进行规划性的控制,在这种控制下丢弃或者打印LogMessages。
示例代码
本技巧的示例代码示范了如何设置 Logger, 并使用它发送XML 格式的日志消息,从Web层,通过JMS,到达一个独立JMS 客户。
本技巧的示例代码包含一个servlet 和一个独立客户端端。servlet 称作 LogDemoServlet。独立客户端端称作LoggingReceiver。独立客户端端监听正发布给JMS主题的消息, 并打印接收到任何TextMessages。示例应用包含一个用户向LogDemoServlet张贴数据的HTML表单。
注意, 表单提示用户下载一个JAR文件,然后运行该文件作为一个独立客户端。在通过表单张贴数据之前,用户需要完成这些步骤。
servlet 通过向Topic发布一条XML 格式的消息来作出响应。如果 LoggingReceiver正在运行, 用户会看到在屏幕上打印出一条XML 格式的消息。
例如:
<?xml version="1.0" encoding="MacRoman" standalone="no"?>
<!DOCTYPE log SYSTEM "logger.dtd">
<log>
<record>
<date>2003-05-05T23:51:20</date>
<millis>1052200280257</millis>
<sequence>0</sequence>
<logger>com.elucify.tips.may2003.LogDemoServlet</logger>
<level>INFO</level>
<class>com.elucify.tips.may2003.LogDemoServlet</class>
<method>doPost</method>
<thread>10</thread>
<message>quest= { 'Johnny' }
FavoriteColor= { 'Violet' }
name= { 'Johnny' }
</message>
</record>
示例代码中的关键部分是servlet LogDemoServlet。这个servlet 简单地将它的POST参数格式化为一个字符串, 并在servlet每次被调用时将该字符记录到Logger 中。方法doPost的开始处设置了 Logger:
// Handle post request
public void doPost(HttpServletRequest req,
HttpServletResponse res)
throws IOException, ServletException {
res.setContentType("text/html");
PrintWriter pw = res.getWriter();
// Get POST parameters and write them as
// "variable=value" to a ByteArrayOutputStream
String postParams = getPostParams(req);
ByteArrayOutputStream bos =
new ByteArrayOutputStream();
Logger logger = Logger.getLogger(
this.getClass().getName());
servlet 设置内容类型、获得一个Writer, 并格式化POST参量。然后它通过调用静态方法 Logger.getLogger(this.getClass().getName())来创建了一个Logger。这个方法返回该名称现有的Logger, 或者如果不存在的话,就创建一个。代码使用了servlet 类的完全限定名。这是一个公共约定,以确保系统中各种Logger的名称不会发生冲突。
缺省情况下,Logger将它所创建的任何消息写入到标准输出,而不管运行什么代码。例如,Web层消息通常写入Web服务器日志文件。
doPost方法的第二部分向Logger添加一个新的 Handler 。每次消息记录到Logger时, Logger发送消息到两个 Handlers (假设消息的日志级别足够高) 。下面是的doPost方法的第二部分 :
// Format logging messages as XML,
// store in byte array
StreamHandler sh = new StreamHandler(
bos, new XMLFormatter());
logger.addHandler(sh);
logger.log(Level.INFO, postParams);
sh.flush();
// Send contents of buffer as JMS message
// to listeners
publish(bos.toString());
pw.println(
"Logging messages sent to subscribers");
}
StreamHandler 是一个使用Formatter 来将LogRecord 格式化为字符串,并将它写入OutputStream的处理程序。在本例中, OutputStream 是 ByteArrayOutputStream, 因此日志记录结果被写入内存。这个方法还使用一个 XMLFormatter,以便 LogRecords 被写作XML文件 。对addHandler的调用将新的StreamHandler添加到Logger,然后将包含格式化POST参数的日志消息发送到 Logger。Logger 发送消息到它的所有Handlers,包括新的StreamHandler。对flush()的调用确保了所有已写入字节被刷新到ByteArrayOutputStream。最后, ByteArrayOutputStream 被转换成XML字符串。再通过发布方法将字符串发布给一个JMS Topic。关于如何将消息发送到JMS Topic的详细描述(使用同样的发布方法) ,请参看2003年4月15 日那一期的技巧“Publish/Subscribe Messaging With JMS Topics”。
posted on 2005-02-04 11:25
jacky 阅读(347)
评论(0) 编辑 收藏