avatar


25.CAT

什么是CAT

CAT,Central Application Tracking,实时应用监控平台。

Github地址:https://github.com/dianping/cat

CAT的服务端基于Java开发,提供了多种语言的客户端。

在release页面,https://github.com/dianping/cat/releases
我们可以看到三个版本的CAT:

  • v4.0-RC1
  • V3.1.0
  • V3.0.0

其中v4.0-RC1是一个BREAK版本,引入了一些与3.X版本不兼容的内容,核心API保持不变,但高级API发生了变化。

本文会基于V3.1.0进行讨论。

其他的链路监控工具有:PinPoint、SkyWalking等,这些链路监控工具的特点是接入端无代码侵入,而CAT是侵入式的。

服务端安装

环境要求

环境要求如下:

  • 线上服务端部署请使用Linux 2.6以及之上,Mac以及Windows环境可以作为开发环境。
  • 服务端推荐使用JDK7的版本。
  • Maven 3及以上
  • MySQL 5.6,5.7,更高版本MySQL都不建议使用,不清楚兼容性。
  • J2EE容器建议使用Tomcat,推荐版本7.*.或8.0.
  • Hadoop环境可选,一般建议规模较小的公司直接使用磁盘模式,可以申请CAT服务端,500GB磁盘或者更大磁盘,这个磁盘挂载在/data/目录上

在网上,包括在美团官方(https://github.com/dianping/cat/wiki/readme_server),环境要求都是这个。

在我实测中,服务端可以使用JDK8。

下载包

通过如下的地址:
https://github.com/dianping/cat/releases

找到3.1.0版本的包,cat-home.war

cat-home.war

数据库

新建库cat,执行script目录的SQL脚本CatApplication.sql

注意:数据库编码使用utf8mb4,否则可能造成中文乱码等问题。

配置

data目录

  • 对于Linux,在根目录(/)新建/data/目录,注意需要有读写权限。
  • 对于Windows,在CAT的Tomcat启动的盘符的根目录,新建\data\目录。

client.xml

新建/data/appdatas/cat/client.xml,文件内容如下:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<config mode="client">
<servers>
<!--下面的IP地址替换为主机的IP地址-->
<server ip="10.211.55.14" port="2280" http-port="8080"/>
</servers>
</config>

datasources.xml

新建/data/appdatas/cat/datasources.xml,文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>

<data-sources>
<data-source id="cat">
<maximum-pool-size>3</maximum-pool-size>
<connection-timeout>1s</connection-timeout>
<idle-timeout>10m</idle-timeout>
<statement-cache-size>1000</statement-cache-size>
<properties>
<!-- 请替换为真实数据库驱动 -->
<driver>com.mysql.jdbc.Driver</driver>
<!-- 请替换为真实数据库URL及Port -->
<url><![CDATA[jdbc:mysql://10.211.55.14:3306/cat]]></url>
<!-- 请替换为真实数据库用户名 -->
<user>root</user>
<!-- 请替换为真实数据库密码 -->
<password>MySQL@2023</password>
<connectionProperties><![CDATA[useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&socketTimeout=120000]]></connectionProperties>
</properties>
</data-source>
</data-sources>

Tomcat

关于Tomcat的安装方法,可以参考《13.Servlet、Filter和Listener》

我们把上文下载的cat-home.war拷贝到Tomcat目录的webapps目录下。
按照一般部署CAT的惯例,我们还会将cat-home.war重命名为cat.war

启动tomcat:

1
2
cd /usr/local/tomcat/apache-tomcat-8.5.97/bin
./startup.sh

浏览器访问http://10.211.55.14:8080/cat

浏览器

有些资料会建议修改tomcatconf目录下的server.xml,以避免中文乱码。其实如果是8版本的以上的Tomcat不需要修改。

1
2
3
4
5
<Connector port="8080" protocol="HTTP/1.1"
<!-- 增加 URIEncoding="utf-8" -->
URIEncoding="utf-8"
connectionTimeout="20000"
redirectPort="8443" />

服务端配置

上文启动后,显示了出问题的CAT服务端,原因是我们没对服务端进行正确的配置。

用户名和密码

  • 用户名:admin
  • 密码:admin

serverConfigUpdate

依次点击Configs全局系统配置服务端配置,我们将该部分所有IP地址为127.0.0.1内容,均修改为实际的IP地址,然后提交。

服务端配置

routerConfigUpdate

依次点击Configs全局系统配置客户端路由,同样,将该部分所有IP地址为127.0.0.1内容,均修改为实际的IP地址,然后提交。

最后,我们回到http://10.211.55.14:8080/cat,此时CAT服务正常。

编译打包问题

我们下载源码,点击IDEA中的红框所示之处,skip test。

编译打包问题

打包后的war包会位于cat/cat-home/target目录下,cat-home.war

但是自行打的包,存在两个问题:

  1. 每隔一分钟会刷三十次错误,内容如下:
    1
    Can't convert type from class java.time.LocalDateTime to class java.util.Date at class org.unidal.dal.jdbc.entity.DefaultDataObjectAccessor
  2. CAT系统关闭后,可能无法重新启动,除非把cat数据库重置。

暂未找到解决方法。

如果在打包过程中,出现了如下报错:

1
The environment variable JAVA_HOME is not correctly set.

需要设置JAVA_HOME环境变量。

在环境变量设置完成后,需要重启IDEA。

根据其他资料的记载,如果想使用8版本的MySQL,方法如下:

  1. 在CAT的源码/cat/pom.xml,修改为8版本的MySQL
    1
    2
    3
    4
    5
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.28</version>
    </dependency>
  2. 修改/data/appdatas/cat/datasources.xml,数据库驱动设置为:
    1
    <driver>com.mysql.cj.jdbc.Driver</driver>

客户端集成

安装完成后,我们编写一个简单的SpringBoot与Cat整合的案例。

依赖

假设已经存在了一个SpringBoot的项目,添加Maven依赖,示例代码:

1
2
3
4
5
<dependency>
<groupId>com.dianping.cat</groupId>
<artifactId>cat-client</artifactId>
<version>3.1.0</version>
</dependency>

CAT_HOME

如果没有环境变量CAT_HOME的话,配置文件会采用默认的地址/data/appdatas/cat,日志会采用默认的地址/data/applogs/cat

在这里,我们可以配置环境变量CAT_HOME

配置步骤如下:

  • 第一步,Edit Configurations…
    第一步
  • 第二步,Add VM options。
    第二步
  • 第三步,设置环境变量-DCAT_HOME=/Users/kaka/Desktop/cathome/
    第三步

配置

client.xml

/data/appdatas/cat/,或者$CAT_HOME目录下,新建文件client.xml,内容如下

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="config.xsd">
<servers>
<server ip="10.211.55.14" port="2280" http-port="8080" />
</servers>
</config>

app.properties

在项目中新建src/main/resources/META-INF/app.properties文件,内容如下:

1
app.name=【appkey】

注意,appkey只能包含英文字母(a-z, A-Z)、数字(0-9)、下划线(_)和中划线(-)。

在本文,app.properties的内容为:

1
app.name=springboot-cat

测试

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.kakawanyifan.controller;

import com.dianping.cat.Cat;
import com.dianping.cat.message.Event;
import com.dianping.cat.message.Transaction;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class CatController {

@RequestMapping("test")
public String test(){
Transaction t = Cat.newTransaction("URL", "pageName");
try {
Cat.logEvent("URL.Server", "serverIp", Event.SUCCESS, "ip=${serverIp}");
Cat.logMetricForCount("metric.key");
Cat.logMetricForDuration("metric.key", 5);
//让代码抛出异常
int i = 1/0;
t.setStatus(Transaction.SUCCESS);
} catch (Exception e) {
t.setStatus(e);
Cat.logError(e);
} finally {
t.complete();
}
return "hello cat";
}
}

启动SpringBoot项目,访问地址http://[ip:端口]/test,会看到CAT页面上已经有记录了。

客户端API

Transaction

什么是Transaction

Transaction,记录一段代码的执行时间和次数,适用场景:

  1. 时间较长的业务逻辑监控
  2. 跨越系统边界的程序访问行为,比如远程调用,数据库调用等。

使用方法

  • addData,添加额外的数据显示。
    可以调用 addData 多次,添加的数据会被 & 连接起来。
  • setStatus,设置状态,成功可以设置SUCCESS,失败可以设置异常。
  • setDurationInMillis,设置执行耗时(毫秒)
    CAT本身会统计,但我们也可以自定执行耗时。
  • setTimestamp,设置执行时间,指的是开始时间。
  • complete,结束Transaction。
    注意!不要忘记结束transaction,否则会得到一个毁坏的消息树,或者导致内存泄漏。

例子

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.kakawanyifan.controller;

import com.dianping.cat.Cat;
import com.dianping.cat.message.Transaction;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/transaction")
public class TransactionController {

@RequestMapping("/test")
public String test(){

// 开启一个Transaction,类别为test,名称为test
Transaction t = Cat.newTransaction("test","test");

try {
dubbo();
t.setStatus(Transaction.SUCCESS);
} catch (Exception e) {
t.setStatus(e);
Cat.logError(e);
} finally {
t.complete();
}

return "test";
}


private String dubbo(){

// 开启第二个Transaction,类别为dubbo,名称为dubbo
Transaction t = Cat.newTransaction("dubbo", "dubbo");

try {
t.setStatus(Transaction.SUCCESS);
} catch (Exception e) {
t.setStatus(e);
Cat.logError(e);
} finally {
t.complete();
}

return "dubbo";
}

@RequestMapping("/api")
public String api(){

Transaction t = Cat.newTransaction("api", "api");

try {
//设置执行时间1秒
t.setDurationInMillis(1000);
t.setTimestamp(System.currentTimeMillis());

//添加额外数据
t.addData("content");
t.setStatus(Transaction.SUCCESS);
} catch (Exception e) {
t.setStatus(e);
Cat.logError(e);
} finally {
t.complete();
}

return "api";
}
}

在上述代码中,一共开启了三个Transaction。

  1. test,是Controller接收调用。
  2. dubbo,模拟远程调用,dubbotest进行模拟调用。
  3. api,这个Transaction自定了一个耗时。

启动项目,分别访问:http://127.0.0.1:8080/transaction/testhttp://127.0.0.1:8080/transaction/api

点击左侧菜单Transaction报表,选中对应类型的Log View查看调用链关系。

Transaction

如图所示,调用链已经形成,可以看到类型为test的调用了类型为dubbo的方法,分别耗时0.26ms和0.61ms。

调用链路

如图所示,调用耗时已经被手动修改成了1000ms,并且添加了额外的信息content。

api

Event

什么是Event

Event,事件,比如记录系统异常,它和transaction相比缺少了时间的统计,开销比transaction要小。

使用方法

Cat.logEvent

Cat.logEvent,记录事件。

1
Cat.logEvent("URL.Server", "事件Key", Event.SUCCESS, "事件内容Value");

Cat.logError

Cat.logError,记录一个带有错误堆栈信息的logError

logError是一种特殊的事件,其type取决于传入的Throwable e,同时错误堆栈信息会被收集并写入data属性中。

  1. 如果e是一个Errortype会被设置为Error
  2. 如果e是一个RuntimeExceptiontype会被设置为RuntimeException
  3. 其他情况下,type会被设置为Exception

示例代码:

1
2
3
4
5
try {
int i = 1 / 0;
} catch (Throwable e) {
Cat.logError(e);
}

我们可以向错误堆栈顶部添加我们自己的错误消息,方法如下:

1
Cat.logError("【我们自己的错误信息】", e);

例子

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.kakawanyifan.controller;

import com.dianping.cat.Cat;
import com.dianping.cat.message.Event;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/event")
public class EventController {
@RequestMapping("/logEvent")
public String logEvent() {
Cat.logEvent("URL.Server", "事件Key", Event.SUCCESS, "事件内容Value");
return "test";
}

@RequestMapping("/logError")
public String logError() {
try {
int i = 1 / 0;
} catch (Throwable e) {
Cat.logError("【我们自己的错误信息】", e);
}
return "test";
}

}

启动项目,分别访问http://127.0.0.1:8080/event/logEventhttp://127.0.0.1:8080/event/logError

URL.Server和RuntimeException

通过上图可以看到,增加了两个事件:URL.Server和RuntimeException。点开LOG查看。

内容

我们访问结束后,不一定马上能看到,CAT会按照一定的规则汇总统计后再展示出来。

Metric

什么是Metric

Metric,记录业务指标,指标包括记录次数、记录平均值、记录总和,业务指标最低统计粒度为1分钟。

使用方法

Counter

Counter,累加:

1
2
3
// 默认是一 
Cat.logMetricForCount("count");
Cat.logMetricForCount("count", 3);

Duration

Duration,平均值:

1
Cat.logMetricForDuration("duration", 5);

比较

CAT会每分钟聚合metric。

  • 如果我们在同一分钟调用count三次(相同的name),会进行累加,并且一次性上报给服务端。
  • 但是在duration,用取平均。

例子

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.kakawanyifan.controller;

import com.dianping.cat.Cat;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/metric")
public class MetricController {

@RequestMapping("/count")
public String count(){
Cat.logMetricForCount("count",10);
return "test";
}

@RequestMapping("/duration")
public String duration(){
Cat.logMetricForDuration("duration", 10);
return "test";
}
}

启动项目,访问http://127.0.0.1:8080/metric/counthttp://127.0.0.1:8080/metric/duration,各三次。
我们可以看到,count的值是30,duration的值是10。

metric

框架集成

SpringBoot集成

添加如下的配置类,示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.kakawanyifan.cat;

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;

@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;
}
}

Dubbo集成

制作cat-dubbo插件

mvn install

使用IDEA打开CAT源码,找到integration目录,Add as Maven Project,然后使用install命令将插件安装到本地仓库。

Add as Maven Project

jar is missing

我们可能会收到如下的告警:

1
[WARNING] The POM for com.dianping.cat:cat-client:jar:2.0.0 is missing, no dependency information available

修改pom文件,将cat-client的版本修改为3.1.0

1
2
3
4
5
<dependency>
<groupId>com.dianping.cat</groupId>
<artifactId>cat-client</artifactId>
<version>3.1.0</version>
</dependency>

程序包com.dianping.cat.log不存在

我们还可能会收到如下的告警:

1
错误: 程序包com.dianping.cat.log不存在

该部分只是打印两行日志需要,可以自行注释掉相关的代码,或者找到cat-3.1.0/lib/java/pom.xml,用和上文相同的方式,自行编译,install到本地,然后再重新installcat-monitor

引入

接下来我们就可以使用如下依赖自动引入dubbo插件了。

1
2
3
4
5
<dependency>
<groupId>net.dubboclub</groupId>
<artifactId>cat-monitor</artifactId>
<version>0.0.6</version>
</dependency>

服务提供方

引入依赖

新建一个SpringBoot的项目,注意选择Spring Web

Spring Web

需要在pom文件中引入两个依赖:

  1. Dubbo的依赖,dubbo-spring-boot-starter
    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.alibaba.spring.boot</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>2.0.0</version>
    </dependency>
  2. CAT的依赖,cat-monitor
    1
    2
    3
    4
    5
    <dependency>
    <groupId>net.dubboclub</groupId>
    <artifactId>cat-monitor</artifactId>
    <version>0.0.6</version>
    </dependency>

本文使用了dubbo-spring-boot-starter进行dubbo与spring-boot的集成。

Github地址:https://github.com/alibaba/dubbo-spring-boot-starter

application.properties

为了简化环境搭建,采用了本地直接调用的方式,所以将注册中心写成N/A表示不注册到注册中心。

application.properties

1
2
3
4
server.port=8082
spring.application.name=sdcp
spring.dubbo.server=true
spring.dubbo.registry=N/A

app.properties

sdcp/src/main/resources中新建CAT的配置文件app.properties,路径是META-INF/app.properties

app.properties

1
app.name=sdcp

HelloService

HelloService接口,示例代码:

1
2
3
4
5
package com.kakawanyifan.service;

public interface HelloService {
public String hello();
}

HelloServiceImpl实现类,示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.kakawanyifan.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.kakawanyifan.service.HelloService;
import org.springframework.stereotype.Component;

@Service(interfaceClass = HelloService.class)
@Component
public class HelloServiceImpl implements HelloService {

public String hello() {
return "hello cat";
}
}

Application启动类

Application启动类,需要添加@EnableDubboConfiguration注解,示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.kakawanyifan;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubboConfiguration
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

}

服务消费方

引入依赖

与"服务提供方"一样,新建一个SpringBoot的项目,注意选择Spring Web

需要在pom文件中引入两个依赖:

  1. Dubbo的依赖,dubbo-spring-boot-starter
    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.alibaba.spring.boot</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>2.0.0</version>
    </dependency>
  2. CAT的依赖,cat-monitor
    1
    2
    3
    4
    5
    <dependency>
    <groupId>net.dubboclub</groupId>
    <artifactId>cat-monitor</artifactId>
    <version>0.0.6</version>
    </dependency>

application.properties

1
2
server.port=8081
spring.application.name=sdcc

app.properties

1
app.name=sdcc

HelloService接口

简化项目的开发,将HelloService接口在消费方和提供方都编写一份,示例代码:

1
2
3
4
5
package com.kakawanyifan.service;

public interface HelloService {
public String hello();
}

TestController

采用直连而非从注册中心获取服务地址的方式,在@Reference注解中声明

1
url = "dubbo://127.0.0.1:20880"

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.kakawanyifan.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.kakawanyifan.service.HelloService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
@Reference(url = "dubbo://127.0.0.1:20880")
private HelloService helloService;

@GetMapping("/hello")
public String hello(){
return helloService.hello();
}
}

Application启动类

需要添加@EnableDubboConfiguration注解,示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.kakawanyifan;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubboConfiguration
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

}

测试

启动项目,访问地址http://localhost:8081/hello,能看到消费者和服务者两个项目。

两个项目

也能看到调用记录。

调用记录

MyBatis集成

创建项目

关于SpringBoot和MyBatis的集成,可以参考《21.SpringBoot [1/3]》,在这里我们假设已经存在了这个项目。

集成cat-mybatis插件

在CAT的源代码中,找到CatMybatisInterceptor,路径为cat-3.1.0/integration/mybatis/CatMybatisPlugin.java,将其复制到我们的项目中。

编写mybatis-config.xml配置文件,将文件放置在resources目录下,内容如下:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<plugin interceptor="com.kakawanyifan.cat.CatMybatisPlugin"></plugin>
</plugins>
</configuration>

修改application.yml文件,添加mybatisconfig-location相关的内容,示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 8080

spring:
datasource:
url: jdbc:mysql://10.211.55.14:3306/cat
username: root
password: MySQL@2023

# mybatis
mybatis:
config-location: classpath:mybatis-config.xml

测试

请求地址http://127.0.0.1:8080/alerts/1,我们可以在CAT看到记录如下:

SQL-1

特别的,如果我们将sql语句修改为错误的语句,不止能看到具体的sql语句,也可以看到报错的堆栈信息:

SQL-2

SpringAOP集成

使用Spring的AOP技术可以简化我们的埋点操作,通过添加统一注解的方式,使得指定方法被能被CAT监控起来。

引入依赖

新增依赖spring-boot-starter-aop,示例代码:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

通过aliyun构建的项目,可能引入不了spring-boot-starter-aop,引入如下依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.13</version>
</dependency>

创建AOP接口

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.kakawanyifan.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Retention(RUNTIME)
@Target(ElementType.METHOD)
public @interface CatAnnotation {

}

创建AOP处理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.kakawanyifan.aop;


import com.dianping.cat.Cat;
import com.dianping.cat.message.Transaction;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
@Aspect
public class CatAopService {

@Around(value = "@annotation(CatAnnotation)")
public Object aroundMethod(ProceedingJoinPoint pjp) throws Throwable {
MethodSignature joinPointObject = (MethodSignature) pjp.getSignature();
Method method = joinPointObject.getMethod();

Transaction t = Cat.newTransaction("method", method.getName());

try {
Object res = pjp.proceed();
t.setSuccessStatus();
return res;
} catch (Throwable e) {
t.setStatus(e);
Cat.logError(e);
throw e;
} finally {
t.complete();
}

}

}

@CatAnnotation

在方法上添加注解@CatAnnotation,这样对应的方法就能被CAT监控起来。示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.kakawanyifan.controller;

import com.kakawanyifan.aop.CatAnnotation;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/cat")
public class CatController {

@RequestMapping("aop")
@CatAnnotation
public String aop(){
return "aop";
}
}

告警接口

关于告警的配置方法,可以参考官方文档。

https://github.com/dianping/cat/wiki/alarm

在这里,主要讨论告警接口的编写。

例如告警配置如下:

1
2
3
4
5
6
7
<sender id="mail" url="http://localhost:8080/alert/msg" type="post" successCode="200" batchSend="true">
<par id="type=1500"/>
<par id="key=title,body"/>
<par id="re=test@test.com"/>
<par id="to=${receiver}"/>
<par id="value=${title},${content}"/>
</sender>

则对应的controller,示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.kakawanyifan;

import com.dianping.cat.Cat;
import com.dianping.cat.message.Event;
import com.dianping.cat.message.Transaction;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

@RestController
public class AlertController {

@RequestMapping(value = "/alert/msg")
public String sendAlert(@RequestParam String to) {
System.out.println("告警了" +to);
return "200";
}
}

运行结果:

1
告警了testUser1@test.com,testUser2@test.com

报表

关于CAT中的报表,可以参考CAT官网关于报表的描述,这里不赘述。
https://github.com/dianping/cat/wiki/readme_report

文章作者: Kaka Wan Yifan
文章链接: https://kakawanyifan.com/10825
版权声明: 本博客所有文章版权为文章作者所有,未经书面许可,任何机构和个人不得以任何形式转载、摘编或复制。

留言板