集成美团cat监控

集成美团cat监控

最近研究spring集成美团cat监控,关于美团cat环境搭建在上篇博客已介绍,集成美团cat在对代码异常,接口请求调用次数与调用时间,sql执行时间和异常,url访问调用时间和次数进行统计,使代码可以有效的管理,在代码与sql查错和排除优化上有显著的效果,可以提升开发和运维的工作效率。有需要的朋友可以了解下。直接步入主题:

一.引入cat-client 的meven依赖

   	<dependency>
           <groupId>com.dianping.cat</groupId>
           <artifactId>cat-client</artifactId>
           <version>3.0.0</version>
   </dependency>

二.springMVC集成url监控埋点
在web项目的web.xml配置文件中加入如下配置即可:

<filter>
    <filter-name>cat-filter</filter-name>
    <filter-class>com.dianping.cat.servlet.CatFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>cat-filter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

三.springboot集成url监控埋点
因为springboot项目没有web.xml ,所以需要创建cat核心拦截器。

/**
* cat核心拦截器
*/
@Configuration
public class CatConfig {
  
  @Bean
  public CatFeignInterceptor catFeignInterceptor(){
  	return new CatFeignInterceptor();
  }
  @Bean
  public FilterRegistrationBean catFilter(){
      FilterRegistrationBean registration = new FilterRegistrationBean();
      CatServletFilter filter = new CatServletFilter();
      registration.setFilter(filter);
      registration.addUrlPatterns("/*");
      registration.setName("cat-filter");
      registration.setOrder(1);
      return registration;
  }

}

四.集成Mybatis监控埋点

/**
*<p>
*description:
*</p>
* @author andy
* @since 2018年5月6日
* @see 
* mybatis拦截器注入到sqlSessionFactory
*/
@Configuration
@ConditionalOnClass({ EnableTransactionManagement.class, EntityManager.class })
@AutoConfigureAfter({ HikariCPConfig.class })
@MapperScan(basePackages = { "com.axatp.cloud.dao" })
public class MybatisConfig {
  private static Log logger = LogFactory.getLog(MybatisConfig.class);

  @Resource
  private DataSource dataSource;

  @Bean
  @ConditionalOnMissingBean
  public SqlSessionFactory sqlSessionFactory() {
      try {
          SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
          sessionFactory.setDataSource(dataSource);
          sessionFactory.setTypeAliasesPackage("com.axatp.cloud.model");
          sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                  .getResources("classpath:/com/axatp/cloud/mapper/*.xml"));
          sessionFactory.setPlugins(new Interceptor[]{new CatMybatisInterceptor()}); //MyBatis插件
          return sessionFactory.getObject();
      } catch (Exception e) {
          throw new RuntimeException(e);
      }
  }

  @Bean
  @ConditionalOnMissingBean
  public DataSourceTransactionManager transactionManager() {
      return new DataSourceTransactionManager(dataSource);
  }

}


/**
* 对MyBatis进行拦截,添加Cat监控
* 
*/
@Intercepts({
      @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
      @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
      @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
})
public class CatMybatisInterceptor implements Interceptor {

  @Override
  public Object intercept(Invocation invocation) {
      MappedStatement statement = (MappedStatement) invocation.getArgs()[0];
      Configuration configuration = statement.getConfiguration();
      HikariDataSource dataSource = (HikariDataSource) configuration.getEnvironment().getDataSource();
      //记录sql执行处理的时间
      Transaction transaction = Cat.newTransaction(Constants.SQL, statement.getId());
      //记录事件
      Cat.logEvent(Constants.SQL_TYPE, statement.getSqlCommandType().name());
      Cat.logEvent(Constants.SQL_URL, dataSource.getJdbcUrl());
      //得到sql语句
      if (invocation.getArgs().length > 1) {
          Object obj = invocation.getArgs()[1];
          BoundSql boundSql = statement.getBoundSql(obj);
          Cat.logEvent(Constants.SQL_DETAILS, showSql(configuration, boundSql));
      }
      return doFinish(invocation, transaction);
  }

  @Override
  public Object plugin(Object o) {
      if (o instanceof Executor) {
          return Plugin.wrap(o, this);
      }
      return o;
  }

  @Override
  public void setProperties(Properties properties) {
  }

  private Object doFinish(Invocation invocation, Transaction transaction) {
      Object object = null;
      try {
          object = invocation.proceed();
          transaction.setStatus(Transaction.SUCCESS);
      } catch (Exception e) {
          Cat.getProducer().logError(e);
      }
      return object;
  }

  /**
   * 解析sql语句
   *
   * @param configuration Configuration
   * @param boundSql      BoundSql
   * @return String
   */
  private static String showSql(Configuration configuration, BoundSql boundSql) {
      Object parameterObject = boundSql.getParameterObject();
      List<ParameterMapping> 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("\\?", 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("\\?", getParameterValue(obj));
                  } else if (boundSql.hasAdditionalParameter(propertyName)) {
                      Object obj = boundSql.getAdditionalParameter(propertyName);
                      sql = sql.replaceFirst("\\?", getParameterValue(obj));
                  }
              }
          }
      }
      return sql;
  }

  /**
   * 获取 sql 参数值,参数解析
   *
   * @param obj value
   * @return parameter value
   */
  private static String getParameterValue(Object obj) {
      String value;
      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;
  }
}

五. 配置监控的项目名
在需要接入CAT监控平台的项目中新建属性配置文件src/main/resources/META-INF/app.properties。其内容如下:

################## CAT会自动加载此文件 ##################
应用的名称(可以根据此名称在CAT的管理控制台查找对应的信息)

app.name=service

六. 客户端配置
你需要在此项目所在的盘符(即这里的D盘)创建data\appdatas\cat目录,并将client.xml配置文件存放在这个路径中。如作者的客户端配置文件D:\data\appdatas\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="192.1168.23.1" port="2280" http-port="8080" />
</servers>
</config>

七. cat监控平台效果图
集成美团cat监控
八. 笔记总结
本次博客写的笔记匆忙,有些地方集成写的可能不是很详细,不足之处欢迎指导,本人也会在后续持续更新。