原创一个微型的日志工具类
JDK 自带的 java.util.logging 非常简陋,于是我们在此基础上新建 LogHelper 类1,封装一些实用的功能。完整源码在:http://git.oschina.net/sp42/ajaxjs/blob/master/ajaxjs-base/src/com/ajaxjs/util/LogHelper.java
- 封装了三种最常用的方法,分别是 config、info 和 warning 方法,均支持带有多个日志消息的对象参数,warning 支持传入 Throwable 异常的参数。
- 可以定位日志所发生的行数及类 java 源文件的超链接,大大便于调试;
- 通过 FileHandler 实现 WARNING 级别的或以上的日记磁盘记录,按照当前日期命名
一般情况下通过工厂模式创建 LogHelper,执行 LogHelper.getLog() 并传入目标类的 class 引用。
public class TestLogHelper {
// 创建类成员为日志服务
private static final LogHelper log = LogHelper.getLog(TestLogHelper.class);
public void testGetLog() {
// …… 其他代码
log.warning("发生异常!……");
log.info("bar");
log.warning("fooo");
// 带有多个日志消息的对象参数,用 {0},{1},{2} 预留消息位置
log.warning("脚本引擎 {0} 没有 {1}() 这个方法", "js", "foo");
log.warning(new Exception("致命错误!"), "脚本引擎 {0} 没有 {1}() 这个方法", "js", "foo");
}
.......
}
控制台现实结果如下所示。
LogHelper 更多方法的签名,参数 msg_tpl 为信息模版,用 {0},{1},{2} 预留消息位置
public void config(String msg);
public void config(String msg_tpl, Object... params);
public void info(String msg);
public void info(String msg_tpl, Object... params);
public void warning(String msg);
public void warning(String msg_tpl, Object... params);
public void warning(Throwable ex, String msg);
public void warning(Throwable ex, String msg_tpl, Object... params);
为什么 LogHelper 可以打印日志从哪个类的哪个方法来,知道是在哪一行代码上发生的?首先观察 API 原生调用 logger.logp(Level.WARNING, className, getMethodName(), msg),其中 className 是发出日志记录请求的类名,对此 LogHelper 已经把 className 作为属性保存起来了,直接传入即可;而 getMethodName() 是发出日志记录请求的方法名,这是个中的关键。下面是 getMethodName() 的源码。
public class LogHelper {
private String className; // 所在的类名
......
/**
* 获取所在的方法,调用时候
*
* @return 方法名称
*/
private String getMethodName() {
StackTraceElement ste = null;
// Thread.getCurrentThread().getStackTrace() 暴露了当前线程的运行栈信息
for (StackTraceElement _ste : Thread.currentThread().getStackTrace()) {
String clzName = _ste.getClassName();
if (_ste.isNativeMethod() || clzName.equals(Thread.class.getName()) || clzName.equals(getClass().getName()))
continue; // 过滤不要的类
if (clzName.equals(className)) {
ste = _ste;
break;
}
}
if(ste != null) {// 超链接,跳到源码所在行数
return String.format(".%s(%s:%s)", ste.getMethodName(), ste.getFileName(), ste.getLineNumber());
}else{
return null;
}
}
.....
}
Thread.getCurrentThread().getStackTrace() 返回当前线程的运行栈信息,结果是 StackTraceElement[] 数组。java.lang.StackTraceElement 专门用于跟踪堆栈元素的信息,通过其源码可见:
public final class StackTraceElement implements java.io.Serializable {
// Normally initialized by VM (public constructor added in 1.5)
private String declaringClass; // 类名
private String methodName; // 方法名
private String fileName; // 文件名
private int lineNumber; // 行号
……
}
这正好是为当前类名、方法名、文件名、行号等信息准备的。
最后,String.format(“.%s(%s:%s)”, …) 的格式是固定的,只要符合这种格式,控制台就可以输出类的超链接。