Java日志缓存机制的实现2013-12-11 ibm 盛江涛, 李思舒, 赵海兵概述日志技术为产品的质量和服务提供了重要的支撑。JDK 在 1.4 版本以后加入了日志机制,为 Java 开发人员提供了便利。但这种日志机制是基于静态日志级别的,也就是在程序运行前就需设定下来要打 印的日志级别,这样就会带来一些不便。在 JDK 提供的日志功能中,日志级别被细化为 9 级,用以 区分不同日志的用途,用来记录一个错误,或者记录正常运行的信息,又或是记录详细的调试信息。由于日志 级别是静态的,如果日志级别设定过高,低级别的日志难以打印出来,从而导致在错误发生时候,难以去追踪 错误的发生原因,目前常常采用的方式是在错误发生的时候,不得不先调整日志级别到相对低的程度,然后再 去触发错误,使得问题根源得到显现。但是这种发生问题需要改动产品配置,然后重新触发问题进行调试的方 式使得产品用户体验变差,而且有些问题会因为偶发性,环境很复杂等原因很难重新触发。相反,如 果起初就把日志级别调整到比较低,那么日志中间会有大量无用信息,而且当产品比较复杂的时候,会导致产 生的日志文件很大,刷新很快,无法及时的记录有效的信息,甚至成为性能瓶颈,从而降低了日志功能对产品 的帮助。本文借助 Java Logging 中的 MemoryHandler 类将所有级别日志缓存起来,在适当时刻输出 ,来解决这个问题。主要围绕 MemoryHandler 的定义和 logging.properties 文件的处理而展开。实 例依附的场景如下,设想用户需要在产品发生严重错误时,查看先前发生的包含 Exception 的错误信息,以 此作为诊断问题缘由的依据。使用 Java 缓冲机制作出的一个解决方案是,将所有产品运行过程中产生的包含 Exception 的日志条目保存在一个可设定大小的循环缓冲队列中,当严重错误(SEVERE)发生时,将缓冲队列 中的日志输出到指定平台,供用户查阅。Java 日志机制的介绍Java 日志机制在很多文章中都 有介绍,为了便于后面文章部分的理解,在这里再简单介绍一下本文用到的一些关键字。Level:JDK 中定义了 Off、Severe、Warning、Info、Config、Fine、Finer、Finest、All 九个日志级别,定义 Off 为 日志最高等级,All 为最低等级。每条日志必须对应一个级别。级别的定义主要用来对日志的严重程度进行分 类,同时可以用于控制日志是否输出。LogRecord:每一条日志会被记录为一条 LogRecord, 其中存储 了类名、方法名、线程 ID、打印的消息等等一些信息。Logger:日志结构的基本单元。Logger 是以 树形结构存储在内存中的,根节点为 root。com.test(如果存在)一定是 com.test.demo(如果存在)的父 节点,即前缀匹配的已存在的 logger 一定是这个 logger 的父节点。这种父子关系的定义,可以为用户提供 更为自由的控制粒度。因为子节点中如果没有定义处理规则,如级别 handler、formatter 等,那么默认就会 使用父节点中的这些处理规则。Handler:用来处理 LogRecord,默认 Handler 是可以连接成一个链 状,依次对 LogRecord 进行处理。Filter:日志过滤器。在 JDK 中,没有实现。Formatter :它主要用于定义一个 LogRecord 的输出格式。图 1. Java 日志处理流程

图 1 展示了一个 LogRecord 的处理流程。一条日志进入处理流程首先是 Logger,其中定义了可通过的 Level,如果 LogRecord 的 Level 高于 Logger 的等级,则进入 Filter(如果有)过滤。如果没有定义 Level,则使用父 Logger 的 Level。Handler 中过程类似,其中 Handler 也定义了可通过 Level,然后进行 Filter 过滤,通 过如果后面还有其他 Handler,则直接交由后面的 Handler 进行处理,否则会直接绑定到 formatter 上面输 出到指定位置。在实现日志缓存之前,先对 Filter 和 Formatter 两个辅助类进行介绍。FilterFilter 是一个接口,主要是对 LogRecord 进行过滤,控制是否对 LogRecord 进行进 一步处理,其可以绑定在 Logger 下或 Handler 下。只要在 boolean isLoggable(LogRecord)方法 中加上过滤逻辑就可以实现对 logrecord 进行控制,如果只想对发生了 Exception 的那些 log 记录进行记 录,那么可以通过清单 1 来实现,当然首先需要将该 Filter 通过调用 setFilter(Filter)方法或者配置 文件方式绑定到对应的 Logger 或 Handler。清单 1. 一个 Filter 实例的实现
@Overridepublic boolean isLoggable(LogRecord record){ if(record.getThrown()!=null){return true; }else{ return false;} }