1.springBoot+集成CAT监控
1.在pom.xml中加入cat-client 的meven依赖
<dependency> <groupId>com.dianping.catgroupId> <artifactId>cat-clientartifactId> <version>1.3.6version> dependency> <dependency> <groupId>org.unidal.frameworkgroupId> <artifactId>foundation-serviceartifactId> <version>2.5.0version> dependency> <dependency> <groupId>org.codehaus.plexusgroupId> <artifactId>plexus-container-defaultartifactId> <version>1.6version> dependency> <dependency> <groupId>org.codehaus.plexusgroupId> <artifactId>plexus-classworldsartifactId> <version>2.5.1version> dependency> <dependency> <groupId>org.apache.xbeangroupId> <artifactId>xbean-reflectartifactId> <version>3.7version> dependency> <dependency> <groupId>org.codehaus.plexusgroupId> <artifactId>plexus-utilsartifactId> <version>3.0.8version> dependency> <dependency> <groupId>org.aspectjgroupId> <artifactId>aspectjrtartifactId> <version>1.9.4version> dependency> <dependency> <groupId>io.nettygroupId> <artifactId>netty-allartifactId> <version>4.1.39.Finalversion> dependency> <dependency> <groupId>com.google.code.gsongroupId> <artifactId>gsonartifactId> <version>2.8.5version> dependency> <dependency> <groupId>com.dianping.catgroupId> <artifactId>cat-client-mybatisartifactId> <version>2.0.0version> dependency>
2.引入核心过滤器
将CatFilterConfigure.java 放到任意SpringBoot 能扫描到的package下面
import com.dianping.cat.servlet.CatFilter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Description:cat的的核心过滤器 * Author: lxf * Date: 2019/10/11 */ @Configuration public class CatFilterConfigure { @Bean public FilterRegistrationBean catFilter() { FilterRegistrationBean registration = new FilterRegistrationBean(); CatFilter filter = new CatFilter(); registration.setFilter(filter); registration.addUrlPatterns("/*"); registration.setName("cat-filter"); registration.setOrder(1); return registration; } }
3. 配置监控项目名
在需要被监听的项目src/main/resources/META-INF目录下添加配置文件app.properties
文件内容为:
app.name=test (自己定义要和平台定义的CMDB一致即可)
在META-INF下面创建client.xml文件启动项目
xml version="1.0" encoding="UTF-8"?> <config mode="client"> <domain id="test"/> config>
出现下面类似日志说明加载成功。
就可以看到test输出到cat的日志:
2.mybatis集成CAT
1.引入拦截器
将CatMybatisPlugin.java 放到任意SpringBoot 能扫描到的package下面
import com.dianping.cat.Cat; import com.dianping.cat.message.Message; import com.dianping.cat.message.Transaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.plugin.*; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.type.TypeHandlerRegistry; import org.mybatis.spring.transaction.SpringManagedTransaction; import org.springframework.util.ReflectionUtils; import javax.sql.DataSource; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.text.DateFormat; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; /** * 对MyBatis进行拦截,添加Cat监控 * @author Steven */ @Intercepts({ @Signature(method = "query", type = Executor.class, args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(method = "update", type = Executor.class, args = {MappedStatement.class, Object.class}) }) public class CatMybatisPlugin implements Interceptor { private static Log logger = LogFactory.getLog(CatMybatisPlugin.class); //缓存,提高性能 private static final MapsqlURLCache = new ConcurrentHashMap (256); private static final String EMPTY_CONNECTION = "jdbc:mysql://unknown:3306/%s?useUnicode=true"; private Executor target; // druid 数据源的类名称 private static final String DruidDataSourceClassName = "com.alibaba.druid.pool.DruidDataSource"; // dbcp 数据源的类名称 private static final String DBCPBasicDataSourceClassName = "org.apache.commons.dbcp.BasicDataSource"; // dbcp2 数据源的类名称 private static final String DBCP2BasicDataSourceClassName = "org.apache.commons.dbcp2.BasicDataSource"; // c3p0 数据源的类名称 private static final String C3P0ComboPooledDataSourceClassName = "com.mchange.v2.c3p0.ComboPooledDataSource"; // HikariCP 数据源的类名称 private static final String HikariCPDataSourceClassName = "com.zaxxer.hikari.HikariDataSource"; // BoneCP 数据源的类名称 private static final String BoneCPDataSourceClassName = "com.jolbox.bonecp.BoneCPDataSource"; @Override public Object intercept(Invocation invocation) throws Throwable { MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; //得到类名,方法 String[] strArr = mappedStatement.getId().split("\\."); String methodName = strArr[strArr.length - 2] + "." + strArr[strArr.length - 1]; Transaction t = Cat.newTransaction("SQL", methodName); //得到sql语句 Object parameter = null; if (invocation.getArgs().length > 1) { parameter = invocation.getArgs()[1]; } BoundSql boundSql = mappedStatement.getBoundSql(parameter); Configuration configuration = mappedStatement.getConfiguration(); String sql = showSql(configuration, boundSql); //获取SQL类型 SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); Cat.logEvent("SQL.Method", sqlCommandType.name().toLowerCase(), Message.SUCCESS, sql); String s = this.getSQLDatabase(); Cat.logEvent("SQL.Database", s); Object returnObj = null; try { returnObj = invocation.proceed(); t.setStatus(Transaction.SUCCESS); } catch (Exception e) { Cat.logError(e); } finally { t.complete(); } return returnObj; } private DataSource getDataSource() { org.apache.ibatis.transaction.Transaction transaction = this.target.getTransaction(); if (transaction == null) { logger.error(String.format("Could not find transaction on target [%s]", this.target)); return null; } if (transaction instanceof SpringManagedTransaction) { String fieldName = "dataSource"; Field field = ReflectionUtils.findField(transaction.getClass(), fieldName, DataSource.class); if (field == null) { logger.error(String.format("Could not find field [%s] of type [%s] on target [%s]", fieldName, DataSource.class, this.target)); return null; } ReflectionUtils.makeAccessible(field); DataSource dataSource = (DataSource) ReflectionUtils.getField(field, transaction); return dataSource; } logger.error(String.format("---the transaction is not SpringManagedTransaction:%s", transaction.getClass().toString())); return null; } /** * 重写 getSqlURL 方法 * * @author fanlychie (https://github.com/fanlychie) */ private String getSqlURL() { // 客户端使用的数据源 DataSource dataSource = this.getDataSource(); if (dataSource != null) { // 处理常见的数据源 switch (dataSource.getClass().getName()) { // druid case DruidDataSourceClassName: return getDataSourceSqlURL(dataSource, DruidDataSourceClassName, "getUrl"); // dbcp case DBCPBasicDataSourceClassName: return getDataSourceSqlURL(dataSource, DBCPBasicDataSourceClassName, "getUrl"); // dbcp2 case DBCP2BasicDataSourceClassName: return getDataSourceSqlURL(dataSource, DBCP2BasicDataSourceClassName, "getUrl"); // c3p0 case C3P0ComboPooledDataSourceClassName: return getDataSourceSqlURL(dataSource, C3P0ComboPooledDataSourceClassName, "getJdbcUrl"); // HikariCP case HikariCPDataSourceClassName: return getDataSourceSqlURL(dataSource, HikariCPDataSourceClassName, "getJdbcUrl"); // BoneCP case BoneCPDataSourceClassName: return getDataSourceSqlURL(dataSource, BoneCPDataSourceClassName, "getJdbcUrl"); } } return null; } /** * 获取数据源的SQL地址 * * @param dataSource 数据源 * @param runtimeDataSourceClassName 运行时真实的数据源的类名称 * @param sqlURLMethodName 获取SQL地址的方法名称 * * @author fanlychie (https://github.com/fanlychie) */ private String getDataSourceSqlURL(DataSource dataSource, String runtimeDataSourceClassName, String sqlURLMethodName) { Class> dataSourceClass = null; try { dataSourceClass = Class.forName(runtimeDataSourceClassName); } catch (ClassNotFoundException e) {} Method sqlURLMethod = ReflectionUtils.findMethod(dataSourceClass, sqlURLMethodName); return (String) ReflectionUtils.invokeMethod(sqlURLMethod, dataSource); } private String getSQLDatabase() { // String dbName = RouteDataSourceContext.getRouteKey(); String dbName = null; //根据设置的多数据源修改此处,获取dbname if (dbName == null) { dbName = "DEFAULT"; } String url = CatMybatisPlugin.sqlURLCache.get(dbName); if (url != null) { return url; } url = this.getSqlURL();//目前监控只支持mysql ,其余数据库需要各自修改监控服务端 if (url == null) { url = String.format(EMPTY_CONNECTION, dbName); } CatMybatisPlugin.sqlURLCache.put(dbName, url); return url; } /** * 解析sql语句 * * @param configuration * @param boundSql * @return */ public String showSql(Configuration configuration, BoundSql boundSql) { Object parameterObject = boundSql.getParameterObject(); List parameterMappings = boundSql.getParameterMappings(); String sql = boundSql.getSql().replaceAll("[\\s]+", " "); if (parameterMappings.size() > 0 && parameterObject != null) { TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject))); } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); for (ParameterMapping parameterMapping : parameterMappings) { String propertyName = parameterMapping.getProperty(); if (metaObject.hasGetter(propertyName)) { Object obj = metaObject.getValue(propertyName); sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj))); } else if (boundSql.hasAdditionalParameter(propertyName)) { Object obj = boundSql.getAdditionalParameter(propertyName); sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj))); } } } } return sql; } /** * 参数解析 * * @param obj * @return */ private String getParameterValue(Object obj) { String value = null; if (obj instanceof String) { value = "'" + obj.toString() + "'"; } else if (obj instanceof Date) { DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA); value = "'" + formatter.format(new Date()) + "'"; } else { if (obj != null) { value = obj.toString(); } else { value = ""; } } return value; } @Override public Object plugin(Object target) { if (target instanceof Executor) { this.target = (Executor) target; return Plugin.wrap(target, this); } return target; } @Override public void setProperties(Properties properties) { } }
2.将mybatis拦截器注入到sqlSessionFactory
在MybatisConfigurer.java中添加代码
//添加CAT插件 CatMybatisPlugin catMybatisPlugin = new CatMybatisPlugin(); factory.setPlugins(new Interceptor[]{catMybatisPlugin});
就可以看到test输出到cat的SQL:
注意:环境规范
Windows环境(具体在哪个盘下,取决于你的项目):
新建D:/data/appdatas/cat/client.xml目录和文件
新建D:/data/applogs/cat/目录
Linux环境:
新建/data/appdatas/cat/client.xml
新建/data/applogs/cat/目录
注意:目录要赋予读写权限
client.xml文件如下:
xml version="1.0" encoding="utf-8"?> <config mode="client" xmlns:xsi="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="config.xsd"> <servers> <server ip="172.30.201.159" port="2280" http-port="8080" /> servers> config>