mybatisplus 打印sql 带拼接好入参的日志

项目中要在控制台查看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;

	}
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值