一般在web工程或者普通java工程中都会用到日志记录工具,java一般使用的是log4j,用法大都是在每个需要记录日志的类上添加一个私有的日志记录器,记录本类中的异常:如下
private final Logger logger = LoggerFactory.getLogger(Test.class);
logger.error("报错!", e);
logger.debug("到达方法");
log4j的配置文件中指定的日志输出格式一般如下 %d{yyyy-MM-dd HH:mm:ss} %-5p [%t] [%c@%L] %m%n
log4j的输出格式可以参考这里 log4j输出格式
输出日志如下
2018-08-25 19:49:05 DEBUG [main] [com.lsg.weblog.logutil.Log@86] 到达方法
可以看到输出的信息有 时间 线程 等级 位置 信息
而每个类自己定义单独一个日志记录器的目的只是为了获取位置而已 ([%c@%L]).为了获取一个位置信息我们必须在每个要记录日志的位置都添加一个记录器对象,这是相当麻烦的.一些时候我们需要在非常简单的java对象如mode vo dto等简单记录一些信息时候突然发现自己还必须在这对象内部添加一个日志记录器,也许会直接导致放弃日志记录的意图
其实位置信息完全可以我们自己来获取,因为原理无非都是去读取线程栈而已.
我们将配置文件中的日志输出格式去掉输出位置信息的那一截 添加自定义的位置变量 seat 变为 %d{yyyy-MM-dd HH:mm:ss} %-5p [%t] [%X{seat}] %m%n
然后创建个自己的日志记录类:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
/**
* 日志记录
*
* @author Norton Lai
* @created 2018-8-25 下午4:41:56
*/
public class Log {
// 源生日志记录对象
private static final Logger log = LoggerFactory.getLogger("custom_log");
// 本对象的路径
private static String thisClassUrl = Log.class.getName();
// 私有构造
private Log() {
}
/**
* 为日志自定义一个变量 seat 日志里可以添加这个位置信息[%X{seat}]
*
* @param s
* @return
* @author Norton Lai
* @created 2018-8-25 下午7:04:34
*/
private static void setSeat() {
MDC.put("seat", getInvokInfo());
}
/**
* 返回此方法的调用者信息.
* 即谁调用此方法就返回调用位置的类名 方法名 行号
* 例: >> net.evecom.cwyun.pdk.vmware.Main.main() 第 102 行 方法会从堆栈最上层往下找到第一个 非 Thread、CkUtil的调用者 ,如果没找到就取最底层的调用者信息
*
* @author Norton Lai
* @created 2017-10-16 下午2:42:43
* @return
*/
private static String getInvokInfo() {
StackTraceElement[] ste = Thread.currentThread().getStackTrace();
String className = null;
String methodName = null;
int lineNumber = 0;
// 从栈的最上开始 往下找 找到第一个不为Log和线程的类
for (int i = 0; i < ste.length; i++) {
className = ste[i].getClassName();
if ("java.lang.Thread".equals(className) || thisClassUrl.equals(className)) {
if (i != (ste.length - 1)) {
continue;// 如果不是最后一个就跳过,如果是最后一个那就没办法了,返回这栈信息吧
}
}
methodName = ste[i].getMethodName();
lineNumber = ste[i].getLineNumber();
break;
}
return className + "." + methodName + "()@" + lineNumber + "";
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void debug(String arg0) {
if (log.isDebugEnabled()) {
setSeat();
log.debug(arg0);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void debug(String arg0, Object arg1) {
if (log.isDebugEnabled()) {
setSeat();
log.debug(arg0, arg1);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void debug(String arg0, Object[] arg1) {
if (log.isDebugEnabled()) {
setSeat();
log.debug(arg0, arg1);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void debug(String arg0, Throwable arg1) {
if (log.isDebugEnabled()) {
setSeat();
log.debug(arg0, arg1);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void error(String arg0) {
if (log.isErrorEnabled()) {
setSeat();
log.error(arg0);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void error(String arg0, Object arg1) {
if (log.isErrorEnabled()) {
setSeat();
log.error(arg0, arg1);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void error(String arg0, Object[] arg1) {
if (log.isErrorEnabled()) {
setSeat();
log.error(arg0, arg1);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void error(String arg0, Throwable arg1) {
if (log.isErrorEnabled()) {
setSeat();
log.error(arg0, arg1);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void info(String arg0) {
if (log.isInfoEnabled()) {
setSeat();
log.info(arg0);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void info(String arg0, Object arg1) {
if (log.isInfoEnabled()) {
setSeat();
log.info(arg0, arg1);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void info(String arg0, Object[] arg1) {
if (log.isInfoEnabled()) {
setSeat();
log.info(arg0, arg1);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void info(String arg0, Throwable arg1) {
if (log.isInfoEnabled()) {
setSeat();
log.info(arg0, arg1);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void trace(String arg0) {
if (log.isTraceEnabled()) {
setSeat();
log.trace(arg0);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void trace(String arg0, Object arg1) {
if (log.isTraceEnabled()) {
setSeat();
log.trace(arg0, arg1);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void trace(String arg0, Object[] arg1) {
if (log.isTraceEnabled()) {
setSeat();
log.trace(arg0, arg1);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void trace(String arg0, Throwable arg1) {
if (log.isTraceEnabled()) {
setSeat();
log.trace(arg0, arg1);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void warn(String arg0) {
if (log.isWarnEnabled()) {
setSeat();
log.warn(arg0);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void warn(String arg0, Object arg1) {
if (log.isWarnEnabled()) {
setSeat();
log.warn(arg0, arg1);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void warn(String arg0, Object[] arg1) {
if (log.isWarnEnabled()) {
setSeat();
log.warn(arg0, arg1);
}
}
/**
* 日志记录
*
* @param arg0
* @author Norton Lai
* @created 2018-8-25 下午7:10:11
*/
public static void warn(String arg0, Throwable arg1) {
if (log.isWarnEnabled()) {
setSeat();
log.warn(arg0, arg1);
}
}
}
其中
private static String getInvokInfo() {
StackTraceElement[] ste = Thread.currentThread().getStackTrace();
String className = null;
String methodName = null;
int lineNumber = 0;
// 从栈的最上开始 往下找 找到第一个不为Log和线程的类
for (int i = 0; i < ste.length; i++) {
className = ste[i].getClassName();
if ("java.lang.Thread".equals(className) || thisClassUrl.equals(className)) {
if (i != (ste.length - 1)) {
continue;// 如果不是最后一个就跳过,如果是最后一个那就没办法了,返回这栈信息吧
}
}
methodName = ste[i].getMethodName();
lineNumber = ste[i].getLineNumber();
break;
}
return className + "." + methodName + "()@" + lineNumber ;
}
代替了[%c@%L] 的功能,可以获取到相同的位置信息
这样再也不需要在其他类定义日志记录器了,直接Log.debug(“xxx”)即可,
可以比对下输出的日志格式:
原本
2018-08-25 19:49:05 DEBUG [main] [com.lsg.weblog.logutil.Log@86] 到达方法
现在
2018-08-25 19:49:05 DEBUG [main] [com.lsg.weblog.test.Test.a()@63] aaaa
几乎没有什么差别,而且完全不用担心性能问题,因为他原本也一样是通过读取栈去获取位置信息的.
这样我们就可以在工程的任何位置随心所欲的添加日志记录了
**如发现所使用的log4j版本中logger的父类 Category的callAppenders 方法中存在同步锁如下
public void callAppenders(LoggingEvent event) {
int writes = 0;
for(Category c = this; c != null; c=c.parent) {
// Protected against simultaneous call to addAppender, removeAppender,...
synchronized(c) {
if(c.aai != null) {
writes += c.aai.appendLoopOnAppenders(event);
}
if(!c.additive) {
break;
}
}
}
if(writes == 0) {
repository.emitNoAppenderWarning(this);
}
}
则必须自己改源码去掉此同步锁,或者继承于logger自己写个子类 去掉同步锁,此锁作用只是为了防止在调用方法同时调用了添加删除append的方法.一般人压根不会去这么操作,这个锁作用不大,但及易引起死锁,减慢速度,
如果使用的log4j版本存在此锁,但没有人为去去除,那么使用此工具类可能会减慢系统运行速度!
java工程中封装静态的log4j日志工具类2