PerformanceInterceptor.java 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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. /**
  52. * SQL 执行最大时长,超过自动停止运行,有助于发现问题。
  53. */
  54. private long maxTime = 0;
  55. /**
  56. * SQL 是否格式化
  57. */
  58. private boolean format = false;
  59. /**
  60. * 是否写入日志文件<br>
  61. * true 写入日志文件,不阻断程序执行!<br>
  62. * 超过设定的最大执行时长异常提示!
  63. */
  64. private boolean writeInLog = false;
  65. private Method oracleGetOriginalSqlMethod;
  66. public Object intercept(Invocation invocation) throws Throwable {
  67. Statement statement;
  68. Object firstArg = invocation.getArgs()[0];
  69. if (Proxy.isProxyClass(firstArg.getClass())) {
  70. statement = (Statement) SystemMetaObject.forObject(firstArg).getValue("h.statement");
  71. } else {
  72. statement = (Statement) firstArg;
  73. }
  74. try {
  75. statement.getClass().getDeclaredField("stmt");
  76. statement = (Statement) SystemMetaObject.forObject(statement).getValue("stmt.statement");
  77. } catch (Exception e) {
  78. // do nothing
  79. }
  80. String originalSql = null;
  81. String stmtClassName = statement.getClass().getName();
  82. if ("oracle.jdbc.driver.T4CPreparedStatement".equals(stmtClassName)) {
  83. try {
  84. if (oracleGetOriginalSqlMethod != null) {
  85. Object stmtSql = oracleGetOriginalSqlMethod.invoke(statement);
  86. if (stmtSql != null && stmtSql instanceof String) {
  87. originalSql = (String) stmtSql;
  88. }
  89. } else {
  90. Class<?> clazz = Class.forName("oracle.jdbc.driver.OracleStatement");
  91. oracleGetOriginalSqlMethod = clazz.getDeclaredMethod("getOriginalSql", (Class<?>) null);
  92. if (oracleGetOriginalSqlMethod != null) {
  93. Object stmtSql = oracleGetOriginalSqlMethod.invoke(statement);
  94. if (stmtSql != null && stmtSql instanceof String) {
  95. originalSql = (String) stmtSql;
  96. }
  97. }
  98. }
  99. } catch (Exception e) {//ignore
  100. }
  101. }
  102. if (originalSql == null) {
  103. originalSql = statement.toString();
  104. }
  105. int index = originalSql.indexOf(':');
  106. if (index > 0) {
  107. originalSql = originalSql.substring(index + 1, originalSql.length());
  108. }
  109. // 计算执行 SQL 耗时
  110. long start = SystemClock.now();
  111. Object result = invocation.proceed();
  112. long timing = SystemClock.now() - start;
  113. // 格式化 SQL 打印执行结果
  114. Object target = PluginUtils.realTarget(invocation.getTarget());
  115. MetaObject metaObject = SystemMetaObject.forObject(target);
  116. MappedStatement ms = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
  117. StringBuilder formatSql = new StringBuilder();
  118. formatSql.append(" Time:").append(timing);
  119. formatSql.append(" ms - ID:").append(ms.getId());
  120. formatSql.append("\n Execute SQL:").append(SqlUtils.sqlFormat(originalSql, format)).append("\n");
  121. if (this.isWriteInLog()) {
  122. if (this.getMaxTime() >= 1 && timing > this.getMaxTime()) {
  123. logger.error(formatSql.toString());
  124. } else {
  125. logger.debug(formatSql.toString());
  126. }
  127. } else {
  128. System.err.println(formatSql.toString());
  129. if (this.getMaxTime() >= 1 && timing > this.getMaxTime()) {
  130. throw new MybatisPlusException(" The SQL execution time is too large, please optimize ! ");
  131. }
  132. }
  133. return result;
  134. }
  135. public Object plugin(Object target) {
  136. if (target instanceof StatementHandler) {
  137. return Plugin.wrap(target, this);
  138. }
  139. return target;
  140. }
  141. public void setProperties(Properties prop) {
  142. String maxTime = prop.getProperty("maxTime");
  143. String format = prop.getProperty("format");
  144. if (StringUtils.isNotEmpty(maxTime)) {
  145. this.maxTime = Long.parseLong(maxTime);
  146. }
  147. if (StringUtils.isNotEmpty(format)) {
  148. this.format = Boolean.valueOf(format);
  149. }
  150. }
  151. public long getMaxTime() {
  152. return maxTime;
  153. }
  154. public void setMaxTime(long maxTime) {
  155. this.maxTime = maxTime;
  156. }
  157. public boolean isFormat() {
  158. return format;
  159. }
  160. public void setFormat(boolean format) {
  161. this.format = format;
  162. }
  163. public boolean isWriteInLog() {
  164. return writeInLog;
  165. }
  166. public void setWriteInLog(boolean writeInLog) {
  167. this.writeInLog = writeInLog;
  168. }
  169. }