CatMybatisPlugin.java 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. package com.wanda.cat.sample.plugins;
  2. import com.alibaba.druid.pool.DruidDataSource;
  3. import com.dianping.cat.Cat;
  4. import com.dianping.cat.message.Message;
  5. import com.dianping.cat.message.Transaction;
  6. import org.apache.commons.dbcp.BasicDataSource;
  7. import org.apache.ibatis.executor.Executor;
  8. import org.apache.ibatis.mapping.BoundSql;
  9. import org.apache.ibatis.mapping.MappedStatement;
  10. import org.apache.ibatis.mapping.ParameterMapping;
  11. import org.apache.ibatis.mapping.SqlCommandType;
  12. import org.apache.ibatis.plugin.*;
  13. import org.apache.ibatis.reflection.MetaObject;
  14. import org.apache.ibatis.session.Configuration;
  15. import org.apache.ibatis.session.ResultHandler;
  16. import org.apache.ibatis.session.RowBounds;
  17. import org.apache.commons.logging.Log;
  18. import org.apache.commons.logging.LogFactory;
  19. import org.apache.ibatis.type.TypeHandlerRegistry;
  20. import org.mybatis.spring.transaction.SpringManagedTransaction;
  21. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
  22. import org.springframework.util.ReflectionUtils;
  23. import java.lang.reflect.Field;
  24. import java.lang.reflect.Method;
  25. import java.text.DateFormat;
  26. import java.util.*;
  27. import java.util.concurrent.ConcurrentHashMap;
  28. import java.util.regex.Matcher;
  29. /**
  30. * 对MyBatis进行拦截,添加Cat监控
  31. * 目前仅支持RoutingDataSource和Druid组合配置的数据源
  32. *
  33. * @author Steven
  34. */
  35. @Intercepts({
  36. @Signature(method = "query", type = Executor.class, args = {
  37. MappedStatement.class, Object.class, RowBounds.class,
  38. ResultHandler.class }),
  39. @Signature(method = "update", type = Executor.class, args = { MappedStatement.class, Object.class })
  40. })
  41. public class CatMybatisPlugin implements Interceptor {
  42. private static Log logger = LogFactory.getLog(CatMybatisPlugin.class);
  43. //缓存,提高性能
  44. private static final Map<String, String> sqlURLCache = new ConcurrentHashMap<String, String>(256);
  45. private static final String EMPTY_CONNECTION = "jdbc:mysql://unknown:3306/%s?useUnicode=true";
  46. private Executor target;
  47. @Override
  48. public Object intercept(Invocation invocation) throws Throwable {
  49. MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
  50. //得到类名,方法
  51. String[] strArr = mappedStatement.getId().split("\\.");
  52. String methodName = strArr[strArr.length - 2] + "." + strArr[strArr.length - 1];
  53. Transaction t = Cat.newTransaction("SQL", methodName);
  54. //得到sql语句
  55. Object parameter = null;
  56. if(invocation.getArgs().length > 1){
  57. parameter = invocation.getArgs()[1];
  58. }
  59. BoundSql boundSql = mappedStatement.getBoundSql(parameter);
  60. Configuration configuration = mappedStatement.getConfiguration();
  61. String sql = showSql(configuration, boundSql);
  62. //获取SQL类型
  63. SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
  64. Cat.logEvent("SQL.Method", sqlCommandType.name().toLowerCase(), Message.SUCCESS, sql);
  65. String s = this.getSQLDatabase();
  66. Cat.logEvent("SQL.Database", s);
  67. Object returnObj = null;
  68. try {
  69. returnObj = invocation.proceed();
  70. t.setStatus(Transaction.SUCCESS);
  71. } catch (Exception e) {
  72. t.setStatus(e);
  73. Cat.logError(e);
  74. } finally {
  75. t.complete();
  76. }
  77. return returnObj;
  78. }
  79. private javax.sql.DataSource getDataSource() {
  80. org.apache.ibatis.transaction.Transaction transaction = this.target.getTransaction();
  81. if (transaction == null) {
  82. logger.error(String.format("Could not find transaction on target [%s]", this.target));
  83. return null;
  84. }
  85. if (transaction instanceof SpringManagedTransaction) {
  86. String fieldName = "dataSource";
  87. Field field = ReflectionUtils.findField(transaction.getClass(), fieldName, javax.sql.DataSource.class);
  88. if (field == null) {
  89. logger.error(String.format("Could not find field [%s] of type [%s] on target [%s]",
  90. fieldName, javax.sql.DataSource.class, this.target));
  91. return null;
  92. }
  93. ReflectionUtils.makeAccessible(field);
  94. javax.sql.DataSource dataSource = (javax.sql.DataSource) ReflectionUtils.getField(field, transaction);
  95. return dataSource;
  96. }
  97. logger.error(String.format("---the transaction is not SpringManagedTransaction:%s", transaction.getClass().toString()));
  98. return null;
  99. }
  100. private String getSqlURL() {
  101. javax.sql.DataSource dataSource = this.getDataSource();
  102. if (dataSource == null) {
  103. return null;
  104. }
  105. if (dataSource instanceof AbstractRoutingDataSource) {
  106. String methodName = "determineTargetDataSource";
  107. Method method = ReflectionUtils.findMethod(AbstractRoutingDataSource.class, methodName);
  108. if (method == null) {
  109. logger.error(String.format("---Could not find method [%s] on target [%s]",
  110. methodName, dataSource));
  111. return null;
  112. }
  113. ReflectionUtils.makeAccessible(method);
  114. javax.sql.DataSource dataSource1 = (javax.sql.DataSource) ReflectionUtils.invokeMethod(method, dataSource);
  115. if (dataSource1 instanceof DruidDataSource) {
  116. DruidDataSource druidDataSource = (DruidDataSource) dataSource1;
  117. return druidDataSource.getUrl();
  118. } else {
  119. logger.error("---only surpport DruidDataSource:" + dataSource1.getClass().toString());
  120. }
  121. } else if(dataSource instanceof BasicDataSource){
  122. return ((BasicDataSource) dataSource).getUrl();
  123. }
  124. return null;
  125. }
  126. private String getSQLDatabase() {
  127. // String dbName = RouteDataSourceContext.getRouteKey();
  128. String dbName = null; //根据设置的多数据源修改此处,获取dbname
  129. if (dbName == null) {
  130. dbName = "DEFAULT";
  131. }
  132. String url = CatMybatisPlugin.sqlURLCache.get(dbName);
  133. if (url != null) {
  134. return url;
  135. }
  136. url = this.getSqlURL();//目前监控只支持mysql ,其余数据库需要各自修改监控服务端
  137. if (url == null) {
  138. url = String.format(EMPTY_CONNECTION, dbName);
  139. }
  140. CatMybatisPlugin.sqlURLCache.put(dbName, url);
  141. return url;
  142. }
  143. /**
  144. * 解析sql语句
  145. * @param configuration
  146. * @param boundSql
  147. * @return
  148. */
  149. public String showSql(Configuration configuration, BoundSql boundSql) {
  150. Object parameterObject = boundSql.getParameterObject();
  151. List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  152. String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
  153. if (parameterMappings.size() > 0 && parameterObject != null) {
  154. TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
  155. if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
  156. sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject)));
  157. } else {
  158. MetaObject metaObject = configuration.newMetaObject(parameterObject);
  159. for (ParameterMapping parameterMapping : parameterMappings) {
  160. String propertyName = parameterMapping.getProperty();
  161. if (metaObject.hasGetter(propertyName)) {
  162. Object obj = metaObject.getValue(propertyName);
  163. sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
  164. } else if (boundSql.hasAdditionalParameter(propertyName)) {
  165. Object obj = boundSql.getAdditionalParameter(propertyName);
  166. sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
  167. }
  168. }
  169. }
  170. }
  171. return sql;
  172. }
  173. /**
  174. * 参数解析
  175. * @param obj
  176. * @return
  177. */
  178. private String getParameterValue(Object obj) {
  179. String value = null;
  180. if (obj instanceof String) {
  181. value = "'" + obj.toString() + "'";
  182. } else if (obj instanceof Date) {
  183. DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
  184. value = "'" + formatter.format((Date)obj) + "'";
  185. } else {
  186. if (obj != null) {
  187. value = obj.toString();
  188. } else {
  189. value = "";
  190. }
  191. }
  192. return value;
  193. }
  194. @Override
  195. public Object plugin(Object target) {
  196. if (target instanceof Executor) {
  197. this.target = (Executor) target;
  198. return Plugin.wrap(target, this);
  199. }
  200. return target;
  201. }
  202. @Override
  203. public void setProperties(Properties properties) {
  204. }
  205. }