PerformanceInterceptor.java 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /**
  2. * Copyright (c) 2011-2014, hubin (jobob@qq.com).
  3. * <p>
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. * <p>
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. * <p>
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.baomidou.mybatisplus.plugins;
  17. import java.lang.reflect.Method;
  18. import java.lang.reflect.Proxy;
  19. import java.sql.Statement;
  20. import java.util.Properties;
  21. import org.apache.ibatis.executor.statement.StatementHandler;
  22. import org.apache.ibatis.logging.Log;
  23. import org.apache.ibatis.logging.LogFactory;
  24. import org.apache.ibatis.mapping.MappedStatement;
  25. import org.apache.ibatis.plugin.Interceptor;
  26. import org.apache.ibatis.plugin.Intercepts;
  27. import org.apache.ibatis.plugin.Invocation;
  28. import org.apache.ibatis.plugin.Plugin;
  29. import org.apache.ibatis.plugin.Signature;
  30. import org.apache.ibatis.reflection.MetaObject;
  31. import org.apache.ibatis.reflection.SystemMetaObject;
  32. import org.apache.ibatis.session.ResultHandler;
  33. import com.baomidou.mybatisplus.exceptions.MybatisPlusException;
  34. import com.baomidou.mybatisplus.toolkit.PluginUtils;
  35. import com.baomidou.mybatisplus.toolkit.SqlUtils;
  36. import com.baomidou.mybatisplus.toolkit.StringUtils;
  37. import com.baomidou.mybatisplus.toolkit.SystemClock;
  38. /**
  39. * <p>
  40. * 性能分析拦截器,用于输出每条 SQL 语句及其执行时间
  41. * </p>
  42. *
  43. * @author hubin nieqiurong TaoYu
  44. * @Date 2016-07-07
  45. */
  46. @Intercepts({@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
  47. @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
  48. @Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})})
  49. public class PerformanceInterceptor implements Interceptor {
  50. private static final Log logger = LogFactory.getLog(PerformanceInterceptor.class);
  51. private static final String DruidPooledPreparedStatement = "com.alibaba.druid.pool.DruidPooledPreparedStatement";
  52. private static final String T4CPreparedStatement = "oracle.jdbc.driver.T4CPreparedStatement";
  53. private static final String OraclePreparedStatementWrapper = "oracle.jdbc.driver.OraclePreparedStatementWrapper";
  54. /**
  55. * SQL 执行最大时长,超过自动停止运行,有助于发现问题。
  56. */
  57. private long maxTime = 0;
  58. /**
  59. * SQL 是否格式化
  60. */
  61. private boolean format = false;
  62. /**
  63. * 是否写入日志文件<br>
  64. * true 写入日志文件,不阻断程序执行!<br>
  65. * 超过设定的最大执行时长异常提示!
  66. */
  67. private boolean writeInLog = false;
  68. private Method oracleGetOriginalSqlMethod;
  69. private Method druidGetSQLMethod;
  70. @Override
  71. public Object intercept(Invocation invocation) throws Throwable {
  72. Statement statement;
  73. Object firstArg = invocation.getArgs()[0];
  74. if (Proxy.isProxyClass(firstArg.getClass())) {
  75. statement = (Statement) SystemMetaObject.forObject(firstArg).getValue("h.statement");
  76. } else {
  77. statement = (Statement) firstArg;
  78. }
  79. try {
  80. statement = (Statement) SystemMetaObject.forObject(statement).getValue("stmt.statement");
  81. } catch (Exception e) {
  82. // do nothing
  83. }
  84. String originalSql = null;
  85. String stmtClassName = statement.getClass().getName();
  86. if (DruidPooledPreparedStatement.equals(stmtClassName)) {
  87. try {
  88. if (druidGetSQLMethod == null) {
  89. Class<?> clazz = Class.forName(DruidPooledPreparedStatement);
  90. druidGetSQLMethod = clazz.getMethod("getSql");
  91. }
  92. Object stmtSql = druidGetSQLMethod.invoke(statement);
  93. if (stmtSql != null && stmtSql instanceof String) {
  94. originalSql = (String) stmtSql;
  95. }
  96. } catch (Exception ignored) {
  97. }
  98. } else if (T4CPreparedStatement.equals(stmtClassName)
  99. || OraclePreparedStatementWrapper.equals(stmtClassName)) {
  100. try {
  101. if (oracleGetOriginalSqlMethod != null) {
  102. Object stmtSql = oracleGetOriginalSqlMethod.invoke(statement);
  103. if (stmtSql != null && stmtSql instanceof String) {
  104. originalSql = (String) stmtSql;
  105. }
  106. } else {
  107. Class<?> clazz = Class.forName(stmtClassName);
  108. oracleGetOriginalSqlMethod = getMethodRegular(clazz, "getOriginalSql");
  109. if(oracleGetOriginalSqlMethod!=null) {
  110. oracleGetOriginalSqlMethod.setAccessible(true);//OraclePreparedStatementWrapper is not a public class, need set this.
  111. if (oracleGetOriginalSqlMethod != null) {
  112. Object stmtSql = oracleGetOriginalSqlMethod.invoke(statement);
  113. if (stmtSql != null && stmtSql instanceof String) {
  114. originalSql = (String) stmtSql;
  115. }
  116. }
  117. }
  118. }
  119. } catch (Exception e) {
  120. //ignore
  121. }
  122. }
  123. if (originalSql == null) {
  124. originalSql = statement.toString();
  125. }
  126. int index = originalSql.indexOf(':');
  127. if (index > 0) {
  128. originalSql = originalSql.substring(index + 1, originalSql.length());
  129. }
  130. // 计算执行 SQL 耗时
  131. long start = SystemClock.now();
  132. Object result = invocation.proceed();
  133. long timing = SystemClock.now() - start;
  134. // 格式化 SQL 打印执行结果
  135. Object target = PluginUtils.realTarget(invocation.getTarget());
  136. MetaObject metaObject = SystemMetaObject.forObject(target);
  137. MappedStatement ms = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
  138. StringBuilder formatSql = new StringBuilder();
  139. formatSql.append(" Time:").append(timing);
  140. formatSql.append(" ms - ID:").append(ms.getId());
  141. formatSql.append("\n Execute SQL:").append(SqlUtils.sqlFormat(originalSql, format)).append("\n");
  142. if (this.isWriteInLog()) {
  143. if (this.getMaxTime() >= 1 && timing > this.getMaxTime()) {
  144. logger.error(formatSql.toString());
  145. } else {
  146. logger.debug(formatSql.toString());
  147. }
  148. } else {
  149. System.err.println(formatSql.toString());
  150. if (this.getMaxTime() >= 1 && timing > this.getMaxTime()) {
  151. throw new MybatisPlusException(" The SQL execution time is too large, please optimize ! ");
  152. }
  153. }
  154. return result;
  155. }
  156. @Override
  157. public Object plugin(Object target) {
  158. if (target instanceof StatementHandler) {
  159. return Plugin.wrap(target, this);
  160. }
  161. return target;
  162. }
  163. @Override
  164. public void setProperties(Properties prop) {
  165. String maxTime = prop.getProperty("maxTime");
  166. String format = prop.getProperty("format");
  167. if (StringUtils.isNotEmpty(maxTime)) {
  168. this.maxTime = Long.parseLong(maxTime);
  169. }
  170. if (StringUtils.isNotEmpty(format)) {
  171. this.format = Boolean.valueOf(format);
  172. }
  173. }
  174. public long getMaxTime() {
  175. return maxTime;
  176. }
  177. public PerformanceInterceptor setMaxTime(long maxTime) {
  178. this.maxTime = maxTime;
  179. return this;
  180. }
  181. public boolean isFormat() {
  182. return format;
  183. }
  184. public PerformanceInterceptor setFormat(boolean format) {
  185. this.format = format;
  186. return this;
  187. }
  188. public boolean isWriteInLog() {
  189. return writeInLog;
  190. }
  191. public PerformanceInterceptor setWriteInLog(boolean writeInLog) {
  192. this.writeInLog = writeInLog;
  193. return this;
  194. }
  195. public Method getMethodRegular(Class<?> clazz, String methodName) {
  196. if (Object.class.equals(clazz)) {
  197. return null;
  198. }
  199. for (Method method : clazz.getDeclaredMethods()) {
  200. if (method.getName().equals(methodName)) {
  201. return method;
  202. }
  203. }
  204. return getMethodRegular(clazz.getSuperclass(), methodName);
  205. }
  206. }