java 缓存机制_Java日志缓存机制的实现(2)

清单 3

// 只纪录有异常并且高于 pushLevel 的 logRecord

finalLevel level = record.getLevel();

finalThrowable thrown = record.getThrown();

If(level >= pushLevel){

push();

}

MemoryHandler.push 方法的触发条件

Push 方法会导致 MemoryHandler 转储日志到下一 handler,清空 buffer。触发条件可以是但不局限于以下几种,实例中使用的是默认的第一种:

日志条目的 Level 大于或等于当前 MemoryHandler 中默认定义或用户配置的 pushLevel;

外部程序调用 MemoryHandler 的 push 方法;

MemoryHandler 子类可以重载 log 方法或自定义触发方法,在方法中逐一扫描日志条目,满足自定义规则则触发转储日志和清空 buffer 的操作。MemoryHanadler 的可配置属性

表 1.MemoryHandler 可配置属性

属性名

描述

缺省值

继承属性

MemoryHandler.level

MemoryHandler 接受的输入到 buffer 的日志等级

Level.INFO

MemoryHandler.filter

在输入到 buffer 之前,可在 filter 中自定义除日志等级外的其他过滤条件

(Undefined)

MemoryHandler.formatter

指定输入至 buffer 的日志格式

(Undefined)

MemoryHandler.encoding

指定输入至 buffer 的日志编码,在 MemoryHandler 中应用甚少

(Undefined)

私有属性

MemoryHandler.size

以日志条目为单位定义循环 buffer 的大小

1,000

MemoryHandler.push

定义将 buffer 中的日志条目发送至下一个 Handler 的最低 Level(包含)

Level.SEVERE

MemoryHandler.target

在构造函数中指定下一步承接日志的 Handler

(Undefined)

使用方式:

以上是记录产品 Exception 错误日志,以及如何转储的 MemoryHandler 处理的内部细节;接下来给出 MemoryHandler 的一些使用方式。

1. 直接使用 java.util.logging 中的 MemoryHandler

清单4

// 在 buffer 中维护 5 条日志信息

// 仅记录 Level 大于等于 Warning 的日志条目并

// 刷新 buffer 中的日志条目到 fileHandler 中处理

intbufferSize =5;

f = newFileHandler("testMemoryHandler.log");

m = newMemoryHandler(f, bufferSize, Level.WARNING);

myLogger = Logger.getLogger("com.ibm.test");

myLogger.addHandler(m);

myLogger.log(Level.WARNING, “thisis a WARNING log”);

. 自定义

1)反射

思考自定义 MyHandler 继承自 MemoryHandler 的场景,由于无法直接使用作为父类私有属性的 size、buffer 及 buffer 中的 cursor,如果在 MyHandler 中有获取和改变这些属性的需求,一个途径是使用反射。清单 5 展示了使用反射读取用户配置并设置私有属性。

清单5

intm_size;

String sizeString = manager.getProperty(loggerName + ".size");

if(null!= sizeString) {

try{

m_size = Integer.parseInt(sizeString);

if(m_size <=0) {

m_size = BUFFER_SIZE; // default 1000

}

// 通过 java 反射机制获取私有属性

Field f;

f = getClass().getSuperclass().getDeclaredField("size");

f.setAccessible(true);

f.setInt(this, m_size);

f = getClass().getSuperclass().getDeclaredField("buffer");

f.setAccessible(true);

f.set(this,newLogRecord[m_size]);

} catch(Exception e) {

}

}

2)重写

直接使用反射方便快捷,适用于对父类私有属性无频繁访问的场景。思考这样一种场景,默认环形队列无法满足我们存储需求,此时不妨令自定义的 MyMemoryHandler 直接继承 Handler,直接对存储结构进行操作,可以通过清单 6 实现。

清单 6

publicclassMyMemoryHandlerextendsHandler{

// 默认存储 LogRecord 的缓冲区容量

privatestaticfinalintDEFAULT_SIZE =1000;

// 设置缓冲区大小

privateintsize = DEFAULT_SIZE;

// 设置缓冲区

privateLogRecord[] buffer;

// 参考 java.util.logging.MemoryHandler 实现其它部分

...

}

使用 MemoryHandler 时需关注的几个问题

了解了使用 MemoryHandler 实现的 Java 日志缓冲机制的内部细节和外部应用之后,来着眼于两处具体实现过程中遇到的问题:Logger/Handler/LogRecord Level 的传递影响,以及如何在开发 MemoryHandler 过程中处理错误日志。

1. Level 的传递影响

Java.util.logging 中有三种类型的 Level,分别是 Logger 的 Level,Handler 的 Level 和 LogRecord 的 Level. 前两者可以通过配置文件设置。之后将日志的 Level 分别与 Logger 和 Handler 的 Level 进行比较,过滤无须记录的日志。在使用 Java Log 时需关注 Level 之间相互影响的问题,尤其在遍历 Logger 绑定了多个 Handlers 时。如图 3 所示:

图 3. Java Log 中 Level 的传递影响

f15f66d6b0dfb52a53fe6a7ae336f26e.png

Java.util.logging.Logger 提供的 setUseParentHandlers 方法,也可能会影响到最终输出终端的日志显示。这个方法允许用户将自身的日志条目打印一份到 Parent Logger 的输出终端中。缺省会打印到 Parent Logger 终端。此时,如果 Parent Logger Level 相关的设置与自身 Logger 不同,则打印到 Parent Logger 和自身中的日志条目也会有所不同。如图 4 所示:

图 4. 子类日志需打印到父类输出终端

066a4800a4a00edbdbc1850739e92180.png

2. 开发 log 接口过程中处理错误日志

在开发 log 相关接口中调用自身接口打印 log,可能会陷入无限循环。Java.util.logging 中考虑到这类问题,提供了一个 ErrorManager 接口,供 Handler 在记录日志期间报告任何错误,而非直接抛出异常或调用自身的 log 相关接口记录错误或异常。Handler 需实现 setErrorManager() 方法,该方法为此应用程序构造 java.util.logging.ErrorManager 对象,并在错误发生时,通过 reportError 方法调用 ErrorManager 的 error 方法,缺省将错误输出到标准错误流,或依据 Handler 中自定义的实现处理错误流。关闭错误流时,使用 Logger.removeHandler 移除此 Handler 实例。

两种经典使用场景,一种是自定义 MyErrorManager,实现父类相关接口,在记录日志的程序中调用 MyHandler.setErrorManager(new MyEroorManager()); 另一种是在 Handler 中自定义 ErrorManager 相关方法,示例如清单 7:

内容导航

第 1 页:清单1-3 第 2 页:清单4-6

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值