自从开始使用日志组件后, 每个类都是这样子的结构:

public class A {
public static final Logger logger = LoggerFactory.getLogger(A.class);
}

但是每个使用日志的类都要加上这么一段,绝对是个让人心烦的问题。

于是我们开始想要定义这样一个静态工具类:

public class Logger {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Logger.class);
...
public static void debug(...) {
logger.debug(...);
...
}
...
}

这样我们就可以在其他类里用直接Logger.debug("test");来输出日志了。
一切看起来都很美好, 但经过测试之后发现这种方法会有一个很严重的问题:
我们打印的日志通常都会带有调用方的信息, 比如类名、方法名、行号、等信息。其中类名、方法名、行号都是极其关键的信息,然而使用上述的方法输出的日志中,这三个信息都变成了Logger这个类的信息,而不是真正调用者的信息, 这样的错误显然是让人无法忍受的事情。

当然不能就这样了事,既然平时使用日志的方法都能输出正确的信息,那么肯定是有办法可以解决的。我们希望最终输出的日志信息都是完全正确的。

我们发现平时使用的Log4j能准确捕获源代码的所在的类、方法、行。但java并没有提供相应的方法,这似乎很神奇。其实Log4j是通过java错误堆栈来实现的,也就是说通过new一个异常Throwable,然后再捕获,从而得到堆栈信息,在进行分析就可以得到行号等信息了。

所以,我们提出像log4j那样,抛出一个异常,然后捕获分析,从而在我们自己的静态日志工具里实现源代码定位,但是这样就多抛出了一次异常,效率肯定变低了。而且抛出异常过多,引发额外事故的风险也是个大问题。
不管怎么说,这毕竟是一个思路,在尝试着寻找其他能得到堆栈信息的方法时,最后在Thread类中找到了一个getStackTrace()方法可以替代抛出异常的解决办法。
顺便一提,这个方法是jdk1.5版本以后才有的。

import org.slf4j.LoggerFactory;

/**
* Logs日志工具类
* 若要自定义可配置打印出执行的方法名和执行行号位置等信息,请修改生成logger对象的方法
*/
public class Logs {

// 本日志类名
private final static String logClassName = Logs.class.getName();

/**
* 获取最原始被调用的堆栈信息
*/
private static StackTraceElement getCaller() {

// 获取堆栈信息
StackTraceElement[] traceElements = Thread.currentThread().getStackTrace();
if (null == traceElements) {
return null;
}

// 最原始被调用的堆栈信息
StackTraceElement caller = null;

// 循环遍历到日志类标识
boolean isEachLogFlag = false;

// 遍历堆栈信息,获取出最原始被调用的方法信息
for (StackTraceElement element : traceElements) {
// 遍历到日志类
if (element.getClassName().equals(logClassName)) {
isEachLogFlag = true;
}

// 下一个非日志类的堆栈,就是最原始被调用的方法
if (isEachLogFlag) {
if (!element.getClassName().equals(logClassName)) {
caller = element;
break;
}
}
}

return caller;
}

/**
* 自动匹配请求类名,生成logger对象
*/
private static org.slf4j.Logger log() {
// 最原始被调用的堆栈对象
StackTraceElement caller = getCaller();
// 空堆栈处理
if (caller == null) {
return LoggerFactory.getLogger(Logs.class);
}

// 与springboot默认显示日志相同,只显示类名
return LoggerFactory.getLogger(caller.getClassName());
}

/*
* 下列是封装后的方法
* */

public static void debug(String message) {
log().debug(message);
}

public static void debug(String message, Throwable exception) {
log().debug(message, exception);
}

public static void debug(String message, Object object) {
log().debug(message, object);
}

public static void debug(String message, Object... object) {
log().debug(message, object);
}

public static void info(String message) {
log().info(message);
}

public static void info(String message, Throwable exception) {
log().info(message, exception);
}

public static void info(String message, Object object) {
log().info(message, object);
}

public static void info(String message, Object... object) {
log().info(message, object);
}

public static void error(String message) {
log().error(message);
}

public static void error(String message, Throwable exception) {
log().error(message, exception);
}

public static void error(String message, Object object) {
log().error(message, object);
}

public static void error(String message, Object... object) {
log().error(message, object);
}

public static void warn(String message) {
log().warn(message);
}

public static void warn(String message, Throwable exception) {
log().warn(message, exception);
}

public static void warn(String message, Object object) {
log().warn(message, object);
}

public static void warn(String message, Object... object) {
log().warn(message, object);
}


}