清单 7
publicclassMyHandlerextendsHandler{
// 在构造方法中实现 setErrorManager 方法
publicMyHandler(){
......
setErrorManager (newErrorManager() {
publicvoiderror (String msg, Exception ex,intcode) {
System.err.println("Error reported by MyHandler "
+ msg + ex.getMessage());
}
});
}
publicvoidpublish(LogRecord record){
if(!isLoggable(record))return;
try{
// 一些可能会抛出异常的操作
} catch(Exception e) {
reportError ("Error occurs in publish ", e, ErrorManager.WRITE_FAILURE);
}
}
......
}
logging.properties
logging.properties 文件是 Java 日志的配置文件,每一行以“key=value”的形式描述,可以配置日志的全局信息和特定日志配置信息,清单 8 是我们为测试代码配置的 logging.properties。
清单 8. logging.properties 文件示例
#Level 等级 OFF > SEVERE > WARNING > INFO > CONFIG > FINE > FINER > FINEST > ALL
# 为 FileHandler 指定日志级别
java.util.logging.FileHandler.level=WARNING
# 为 FileHandler 指定 formatter
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
# 为自定义的 TestMemoryHandler 指定日志级别
com.ibm.test.MemoryHandler.level=INFO
# 设置 TestMemoryHandler 最多记录日志条数
com.ibm.test.TestMemoryHandler.size=1000
# 设置 TestMemoryHandler 的自定义域 useParentLevel
com.ibm.test.TestMemoryHandler.useParentLevel=WARNING
# 设置特定 log 的 handler 为 TestMemoryHandler
com.ibm.test.handlers=com.ibm.test.TestMemoryHandler
# 指定全局的 Handler 为 FileHandler
handlers=java.util.logging.FileHandler
从 清单 8 中可以看出 logging.properties 文件主要是用来给 logger 指定等级(level),配置 handler 和 formatter 信息。
如何监听 logging.properties
如果一个系统对安全性要求比较高,例如系统需要对更改 logging.properties 文件进行日志记录,记录何时何人更改了哪些记录,那么应该怎么做呢?
这里可以利用 JDK 提供的 PropertyChangeListener 来监听 logging.properties 文件属性的改变。
例如创建一个 LogPropertyListener 类,其实现了 java.benas.PropertyChangeListener 接口,PropertyChangeListener 接口中只包含一个 propertyChange(PropertyChangeEvent)方法,该方法的实现如清 9 所示。
清单 9. propertyChange 方法的实现
@Override
publicvoidpropertyChange(PropertyChangeEvent event) {
if(event.getSource()instanceofLogManager){
LogManager manager=(LogManager)event.getSource();
update(manager);
execute();
reset();
}
}
propertyChange(PropertyChangeEvent)方法中首先调用 update(LogManager)方法来找出 logging.properties 文件中更改的,增加的以及删除的项,这部分代码如清单 10 所示;然后调用 execute() 方法来执行具体逻辑,参见 清单 11;最后调用 reset() 方法对相关属性保存以及清空,如 清单 12 所示。
清单 10. 监听改变的条目
public void update(LogManager manager){
Properties logProps = null ;
// 使用 Java 反射机制获取私有属性
try{
Field f = manager.getClass().getDeclaredField("props");
f.setAccessible(true );
logProps=(Properties)f.get(manager);
}catch (Exception e){
logger.log(Level.SEVERE,"Get private field error.", e);
return;
}
Set logPropsName=logProps.stringPropertyNames();
for(String logPropName:logPropsName){
String newVal=logProps.getProperty(logPropName).trim();
// 记录当前的属性
newProps.put(logPropName, newVal);
// 如果给属性上次已经记录过
if(oldProps.containsKey(logPropName)){
String oldVal = oldProps.get(logPropName);
if(newVal== null ?oldVal== null :newVal.equals(oldVal)){
// 属性值没有改变,不做任何操作
}else{
changedProps.put(logPropName, newVal);
}
oldProps.remove(logPropName);
}else{// 如果上次没有记录过该属性,则其应为新加的属性,记录之
changedProps.put(logPropName, newVal);
}
}
}
代码中 oldProps、newProps 以及 changedProps 都是 HashMap类型,oldProps 存储修改前 logging.properties 文件内容,newProps 存储修改后 logging.properties 内容,changedProps 主要用来存储增加的或者是修改的部分。
方法首先通过 Java 的反射机制获得 LogManager 中的私有属性 props(存储了 logging.properties 文件中的属性信息),然后通过与 oldProps 比较可以得到增加的以及修改的属性信息,最后 oldProps 中剩下的就是删除的信息了。
内容导航
第 1 页:清单1-3 第 2 页:清单4-6