通常地,我们在使用log日志工具类时,除了聚合的专门logName外,我们会在每一个需要日志记录的地方加入以下代码
public final static Logger logger = Logger.getLogger(UserServiceImpl.class);
如此一来,每个类中都充斥着这样的重复代码,不够优雅
很容易想到,可以通过一个LogUtil类来将这个操作归集
public class LogUtil {
public static void log(String msg){
LogManager.getLogger(LogUtil.class)
.debug(msg);
}
}
但是这样会有一个问题,就是打印出来的log,记载的是LogUtil的路径
试着变通,可以这样?
public class LogUtil {
public static void log(Class clazz,String msg){
LogManager.getLogger(clazz).debug(msg);
}
}
这样一来,虽然打印的类路径是正确的,但每一个执行类中都传入其class甚至不如直接在每一个类中定义一个logger来得优雅。
【感谢栈,我们有了一个新的思路!】
基于Java的栈的机制,在当前执行线程的栈中会保存有类的相关信息,我们可以通过这个机制,从栈中取出我们想要的
public class LogUtil {
public static void trace(String msg) {
getLogger().trace(msg);
}
public static void debug(String msg) {
getLogger().debug(msg);
}
public static void info(String msg) {
getLogger().info(msg);
}
public static void warn(String msg) {
getLogger().warn(msg);
}
public static void error(String msg) {
getLogger().error(msg);
}
public static void error(String msg,Throwable t) {
getLogger().error(msg,t);
}
private static Logger getLogger() {
return Logger.getLogger(findCaller().getClassName());
}
private static StackTraceElement findCaller() {
// 获取堆栈信息
StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
// 最原始被调用的堆栈信息
StackTraceElement caller = null;
// 日志类名称
String logClassName = LogUtil.class.getName();
// 循环遍历到日志类标识
int i = 0;
for (int len = callStack.length; i < len; i++) {
if (logClassName.equals(callStack[i].getClassName())) {
break;
}
}
//是的没有错,这是一个magic number!想知道为什么?开启你的堆栈,来寻找我的宝藏吧!by Luo
caller = callStack[i + 3];
return caller;
}
}
这样,我们就可以直接调用LogUtil来将日志打印,同时还不需要传入调用类本身!