项目中要在控制台查看sql 因为mybatis的sql总是有问号占位符 还要手动粘贴填充参数, 所以写了一个logback的日志织入
启动类上添加如下代码
System.setProperty(ClassicConstants.LOGBACK_CONTEXT_SELECTOR,
"com.xxx.xxx.xxx.config.CustomDefaultContextSelector");
其他代码如下
/**
* desc: 日志实体类类
*
* @author :
* creat_date: 2020年10月27日10:44:00
**/
public class SqlLogVO
{
private String prepareSqlStr;
private String parameterStr;
private String completeSqlStr;
/**
* @return the prepareSqlStr
*/
public String getPrepareSqlStr()
{
return prepareSqlStr;
}
/**
* @param prepareSqlStr the prepareSqlStr to set
*/
public void setPrepareSqlStr(String prepareSqlStr)
{
this.prepareSqlStr = prepareSqlStr;
}
/**
* @return the parameterStr
*/
public String getParameterStr()
{
return parameterStr;
}
/**
* @param parameterStr the parameterStr to set
*/
public void setParameterStr(String parameterStr)
{
this.parameterStr = parameterStr;
}
/**
* @return the completeSqlStr
*/
public String getCompleteSqlStr()
{
return completeSqlStr;
}
/**
* @param completeSqlStr the completeSqlStr to set
*/
public void setCompleteSqlStr(String completeSqlStr)
{
this.completeSqlStr = completeSqlStr;
}
@Override
public String toString()
{
return "SqlLogVO [prepareSqlStr=" + prepareSqlStr + ", parameterStr=" + parameterStr + ", completeSqlStr="
+ completeSqlStr + "]";
}
}
/**
* 定义实体类 实现 put(this) 兼容 相同key和相同value 参数
*
*@data 2020年10月27日10:45:05
*/
public class WrapperMybatisLogParamValue
{
private String paramValue;
public WrapperMybatisLogParamValue(String paramValue)
{
super();
this.paramValue = paramValue;
}
/**
* @return the paramValue
*/
public String getParamValue()
{
return paramValue;
}
/**
* @param paramValue the paramValue to set
*/
public void setParamValue(String paramValue)
{
this.paramValue = paramValue;
}
@Override
public String toString()
{
return "CustomerMybatisLogParamValue [paramValue=" + paramValue + "]";
}
}
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.selector.ContextSelector;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* desc:
*
* @author : ckl
* creat_date: 2020年10月27日10:45:42
* creat_time: 10:24
**/
public class CustomDefaultContextSelector implements ContextSelector, MethodInterceptor
{
private LoggerContext defaultLoggerContext;
private LoggerContext proxyedDefaultLoggerContext;
private static ConcurrentHashMap<String, org.slf4j.Logger> cachedLogger = new ConcurrentHashMap<>(1000);
public CustomDefaultContextSelector(LoggerContext context)
{
this.defaultLoggerContext = context;
}
@Override
public LoggerContext getLoggerContext()
{
return getDefaultLoggerContext();
}
@Override
public LoggerContext getDefaultLoggerContext()
{
if (proxyedDefaultLoggerContext == null)
{
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(defaultLoggerContext.getClass());
enhancer.setCallback(this);
proxyedDefaultLoggerContext = (LoggerContext) enhancer.create();
}
return proxyedDefaultLoggerContext;
}
@Override
public LoggerContext detachLoggerContext(String loggerContextName)
{
return defaultLoggerContext;
}
@Override
public List<String> getContextNames()
{
return Arrays.asList(defaultLoggerContext.getName());
}
@Override
public LoggerContext getLoggerContext(String name)
{
if (defaultLoggerContext.getName().equals(name))
{
return defaultLoggerContext;
}
else
{
return null;
}
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable
{
Object result;
result = methodProxy.invokeSuper(o, args);
if (Objects.equals(method.getReturnType().getName(), org.slf4j.Logger.class.getName())
&& Objects.equals(method.getName(), "getLogger"))
{
org.slf4j.Logger logger = (org.slf4j.Logger) result;
String loggerName = logger.getName();
/**
* 只关心mybatis层的logger,mybatis层的logger的包名,我们这边是固定的包下面
* 如果不是这个包下的,直接返回
*/
if (!loggerName.startsWith("com.xxx.xxx.xxx.xxx") || !loggerName.contains(".dao."))
{
return result;
}
/**
* 对mybatis mapper的log,需要进行代理;代理后的对象,我们暂存一下,免得每次都创建代理对象
* 从缓存获取代理logger
*/
if (cachedLogger.get(loggerName) != null)
{
return cachedLogger.get(loggerName);
}
CustomLoggerInterceptor customLoggerInterceptor = new CustomLoggerInterceptor();
customLoggerInterceptor.setLogger((Logger) result);
Object newProxyInstance = Proxy.newProxyInstance(result.getClass().getClassLoader(),
result.getClass().getInterfaces(),
customLoggerInterceptor);
cachedLogger.put(loggerName, (org.slf4j.Logger) newProxyInstance);
return newProxyInstance;
}
return result;
}
public static ConcurrentHashMap<String, org.slf4j.Logger> getCachedLogger()
{
return cachedLogger;
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.slf4j.helpers.BasicMarker;
import com.alibaba.druid.sql.SQLUtils;
import ch.qos.logback.classic.Logger;
/**
* desc:
*
* @author : ckl
* creat_date: 2020年10月27日10:46:17
* creat_time: 10:52
**/
public class CustomLoggerInterceptor implements InvocationHandler
{
private static final ThreadLocal<SqlLogVO> SQL_LOG_VO_THREAD_LOCAL = new ThreadLocal<SqlLogVO>()
{
@Override
protected SqlLogVO initialValue()
{
return new SqlLogVO();
}
};
private static final Set<String> skipMethodSet = new HashSet<String>();
static
{
skipMethodSet.add("isTraceEnabled");
skipMethodSet.add("isDebugEnabled");
skipMethodSet.add("isInfoEnabled");
skipMethodSet.add("isWarnEnabled");
skipMethodSet.add("isErrorEnabled");
}
private Logger logger;
public static Set<String> methods = new LinkedHashSet<>();
public void setLogger(Logger logger)
{
this.logger = logger;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
// 包名
if (logger.getName().startsWith("com.xxx.xxx.xxx.xxx"))
{
if (skipMethodSet.contains(method.getName()))
{
return method.invoke(logger, args);
}
/**
* 先调用原始方法,获取返回值
*/
Object orginResult = method.invoke(logger, args);
String s = assemblyCompleteMybatisQueryLog(args);
if (s != null)
{
String formatMySql = SQLUtils.formatMySql(s);
formatMySql = formatMySql + ";";
formatMySql = "\r\n" + formatMySql;
formatMySql = "\r\n"
+ "-- ----------------------------------------------------------------------------------------"
+ formatMySql + "\r\n";
formatMySql = "\r\n" + formatMySql
+ "-- ----------------------------------------------------------------------------------------"
+ "\r\n";
Object[] objects = new Object[args.length];
System.arraycopy(args, 0, objects, 0, args.length);
for (int i = (objects.length - 1); i >= 0; i--)
{
if (objects[i] != null && objects[i] instanceof String)
{
objects[i] = formatMySql;
break;
}
}
return method.invoke(logger, objects);
}
return orginResult;
}
methods.add(method.getDeclaringClass() + "#" + method.getName());
if (method.getName().equals("info"))
{
System.out.println();
}
return method.invoke(logger, args);
}
private String assemblyCompleteMybatisQueryLog(Object[] args)
{
if (args != null && args.length > 1)
{
if (!(args[0] instanceof BasicMarker))
{
return null;
}
/**
* marker不匹配,直接返回
*/
BasicMarker arg = (BasicMarker) args[0];
if (!Objects.equals(arg.getName(), "MYBATIS"))
{
return null;
}
String message = null;
for (int i = (args.length - 1); i >= 0; i--)
{
if (args[i] != null && args[i] instanceof String)
{
message = (String) args[i];
break;
}
}
if (message == null)
{
return null;
}
if (message.startsWith("==> Preparing:"))
{
String newMessage = message.substring("==> Preparing:".length()).trim();
SQL_LOG_VO_THREAD_LOCAL.get().setPrepareSqlStr(newMessage);
}
else if (message.startsWith("==> Parameters:"))
{
try
{
return populateSqlWithParams(message);
}
catch (Exception e)
{
logger.error("{}", e);
}
finally
{
SQL_LOG_VO_THREAD_LOCAL.remove();
}
}
}
return null;
}
private String populateSqlWithParams(String message)
{
String s = message.substring("==> Parameters:".length()).trim();
String[] params = s.split(",");
if (params.length == 0)
{
SQL_LOG_VO_THREAD_LOCAL.remove();
return null;
}
/**
* 组装参数 会有bug 如果存在同名参数 用hashmap会覆盖
*/
LinkedHashMap<WrapperMybatisLogParamValue, String> paramValueAndTypaMap = new LinkedHashMap<>();
for (String param : params)
{
String[] paramValueAndType = param.split("\\(");
if (paramValueAndType.length != 2)
{
continue;
}
String type = paramValueAndType[1];
/**
* 去掉右边的圆括号
*/
String trimTheRightParenthesis = type.substring(0, type.length() - 1);
paramValueAndTypaMap.put(new WrapperMybatisLogParamValue(paramValueAndType[0]), trimTheRightParenthesis);
}
String prepareSqlStr = SQL_LOG_VO_THREAD_LOCAL.get().getPrepareSqlStr();
if (prepareSqlStr == null)
{
return null;
}
for (Map.Entry<WrapperMybatisLogParamValue, String> entry : paramValueAndTypaMap.entrySet())
{
String type = entry.getValue();
WrapperMybatisLogParamValue paramValueEntity = entry.getKey();
String paramValue = paramValueEntity.getParamValue();
if ("String".equals(type))
{
paramValue = "\"" + paramValue.trim() + "\"";
}
else
{// 统一去空格 参数形式 公司(String), 03(String), 2(Integer), 中间会有空格
paramValue = paramValue.trim();
}
prepareSqlStr = prepareSqlStr.replaceFirst("\\?", paramValue);
}
// System.out.println(prepareSqlStr);
return prepareSqlStr;
}
}