⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

摘要: 原创出处 http://www.iocoder.cn/Spring-Boot/CAT/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

本文在提供完整代码示例,可见 https://github.com/YunaiV/SpringBoot-Labslab-61 目录。

原创不易,给点个 Star 嘿,一起冲鸭!

1. 概述

《芋道 CAT 极简入门》文章中,我们一起完成了 CAT 的学习,并完成了 CAT 服务器的搭建。

本文,我们将使用 CAT 提供的 Java 客户端 客户端,接入 Spring Boot 应用中,实现应用监控的功能。

2. 快速入门

示例代码对应仓库:lab-61-demo

在 CAT 中,一共有四种监控模型:

  • Transaction:适合记录跨越系统边界的程序访问行为,比如远程调用,数据库调用,也适合执行时间较长的业务逻辑监控。Transaction 用来记录一段代码的执行时间次数
  • Event:用来记录一件事发生的次数,比如记录系统异常。它和 Transaction 相比缺少了时间的统计,开销比 Transaction 要小。
  • Heartbeat:表示程序内定期产生的统计信息, 如 CPU 利用率、内存利用率、连接池状态、系统负载等。
  • Metric:用于记录业务指标、指标可能包含对一个指标记录次数、记录平均值、记录总和,业务指标最低统计粒度为 1 分钟。

本小节,我们会调用 CAT 客户端的 API,生成相应监控模型的监控数据,最终上传给 CAT 服务端。

下面,新建 lab-61-demo 项目,进行 CAT 使用的演示。最终项目如下图所示:

项目结构

2.1 引入依赖

创建 pom.xml 文件,引入 cat-client 依赖。完整内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>lab-61</artifactId>
<groupId>cn.iocoder.springboot.labs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>lab-61-demo</artifactId>

<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<spring.boot.version>2.2.4.RELEASE</spring.boot.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- 引入 SpringMVC 相关依赖,并实现对其的自动配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 引入 CAT 相关依赖 -->
<dependency>
<groupId>com.dianping.cat</groupId>
<artifactId>cat-client</artifactId>
<version>3.0.0</version>
</dependency>
</dependencies>

<!-- 引入私服,因为 CAT 依赖并没有发到 Maven 中央仓库 -->
<repositories>
<repository>
<id>central</id>
<name>Maven2 Central Repository</name>
<layout>default</layout>
<url>http://repo1.maven.org/maven2</url>
</repository>
<repository>
<id>unidal.releases</id>
<url>http://unidal.org/nexus/content/repositories/releases/</url>
</repository>
</repositories>

</project>

友情提示:因为 cat-client 并未上传到中央 Maven 仓库,所以需要引入额外的私服。

2.2 配置文件

resources 目录下,创建 META-INF 目录,再在其下创建 app.properties 配置文件,它是 CAT 客户端的配置文件。完整内容如下:

app.name=demo-application

app.name 配置项,设置应用名。

2.3 环境配置

我们需要通过环境配置,设置使用的 CAT 服务器地址。命令行操作如下:

友情提示:艿艿本地的环境是 MacOS,所以如下的教程同样适用于 Linux 的胖友。

如果使用 Windows 的话,使用项目所在硬盘为根目录。例如说,D 盘则对应 D:\data\appdatas\cat\D:\data\applogs\cat\

# 创建 CAT 配置目录
$ sudo mkdir -p /data/appdatas/cat

# 创建 CAT 日志目录
$ sudo mkdir -p /data/applogs/cat

# 赋予权限
$ sudo chmod 777 /data/appdatas/cat
$ sudo chmod 777 /data/applogs/cat

然后,在 /data/appdatas/cat 目录,创建 CAT 客户端配置文件 client.xml。具体内容如下:

<?xml version="1.0" encoding="utf-8"?>
<config mode="client">
<servers>
<!-- CAT 服务器 IP -->
<server ip="172.16.48.185" port="2280" http-port="8080"/>
</servers>
</config>

注意,<server /> 标签中填写胖友 CAT 服务器所在地址哈。

2.4 Application

创建 Application 类,一个平凡的 Spring Boot 引用的启动类。代码如下:

@SpringBootApplication
public class Application {

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

}

下面,我们创建 DemoController 类,在其中编写 CAT 客户端 API 的使用示例。

2.5 监控模型 Transaction 示例

在 DemoController 中,编写代码如下,编写 Transaction 的使用示例:

// DemoController.java

@GetMapping("/transaction")
public String transaction() {
// <1> 创建 Transaction 对象
Transaction t = Cat.newTransaction("URL", "/demo/transaction");
try {
// <2> ... yourBusiness(); 业务逻辑

// <3> 设置 Transaction 的状态为成功
t.setStatus(Transaction.SUCCESS);
} catch (Exception e) {
// <4> 设置 Transaction 的状态为异常
t.setStatus(e);
} finally {
// <5> 标记 Transaction 结束
t.complete();
}
return "success";
}

<1> 处,调用 Cat 的 #newTransaction(String type, String name) 方法,创建 Transaction 对象。

  • type 参数:表示 Transaction 分类。这里,设置为 URL
  • name 参数:表示 Transaction 名字。这里,设置为 /demo/transaction 地址。

友情提示:可能胖友对 typename 参数有懵逼,稍后我们结合 CAT 控制台一起看看即可明白~

<2> 处,这里放置我们的业务逻辑的代码。

<3> 处,在逻辑执行成功时,调用 Transaction 的 #setStatus(String status) 方法,设置状态为 Transaction.SUCCESS 成功

<4> 处,在逻辑执行失败时,调用 Transaction 的 #setStatus(Throwable e) 方法,设置状态失败

友情提示:在 CAT 的设计中,status 属性的类型是 String 字符串,所以在传入 e 异常时,会使用异常类的类名作为 status 属性的值。

<5> 处,在逻辑执行结束时,调用 Transaction 的 #complete() 方法,标记 Transaction 结束。


下面,我们执行 Application 启动示例项目。在项目的启动日志中,我们并未看到 CAT 相关的内容,因为 CAT 客户端是懒加载,在真正使用到的地方自动初始化

启动完成后,调用 3 次 http://127.0.0.1:8080/demo/transaction 接口,创建 3 次 Transaction 的监控数据。

然后,我们来看看 CAT 控制台的监控数据。如下图所示:

CAT 监控数据 - Transaction - 分类列表

之后,点击 URL 进行下钻,查看 URL 分类 具体有哪些。如下图所示:

CAT 监控数据 - Transaction - 分类名字

至此我们可以发现,分类 type 是 Transaction 的一级分类,名字 name 是 Transaction 的二级分类,且二者是上下级关系。


下面,我们再来补充一点 Transaction 的其它小知识。Transaction API 除了上述几个之外,还有如下四个:

  • addData
  • setDurationStart
  • setDurationInMillis
  • setTimestamp

Transaction t = Cat.newTransaction("URL", "pageName");

try {
t.setDurationInMillis(1000);
t.setTimestamp(System.currentTimeMillis());
t.setDurationStart(System.currentTimeMillis() - 1000);
t.addData("content");
t.setStatus(Transaction.SUCCESS);
} catch (Exception e) {
t.setStatus(e);
Cat.logError(e);
} finally {
t.complete();
}

在使用 Transaction API 时,你可能需要注意以下几点:

  • 我们可以调用 #addData(String keyValuePairs) 方法多次,添加的数据会被 & 连接起来。
  • 同时指定 durationdurationStart 是没有意义的,尽管我们在样例中这样做了。
  • 不要忘记标记 Transaction 完成!否则你会得到一个毁坏的消息树以及内存泄漏!

2.6 监控模型 Event 示例

Event 提供了三类 API,我们分成三个小节来瞅瞅。

友情提示:稍后胖友看完会发现,虽然是三类 API,实际是正常异常的两类 Event。

2.6.1 logEvent 示例

在 DemoController 中,编写代码如下,编写 Event 的使用示例:

// DemoController.java

@GetMapping("/event-01")
public String event01() {
// Cat.logEvent("URL.Server", "127.0.0.1");
Cat.logEvent("URL.Server", "127.0.0.1", Event.SUCCESS, "data");
return "success";
}

调用 Cat 的 #logEvent(String type, String name, String status, String nameValuePairs) 方法,记录一次事件。

  • type 参数:表示 Event 分类。这里,设置为 URL.Server
  • name 参数:表示 Event 名字。这里,设置为 127.0.0.1
  • status 参数:表示 Event 状态。这里,设置为 Event.SUCCESS 成功。
  • nameValuePairs 参数,表示 Event 附加数据。这里,设置为 nameValuePairs

友情提示:可能胖友对 typename 参数有懵逼,稍后我们结合 CAT 控制台一起看看即可明白~

😈 从 API 也可以看出,Transaction 和 Event 监控模型是比较接近的,相差就在时间属性上。


下面,我们执行 Application 启动示例项目。启动完成后,调用 3 次 http://127.0.0.1:8080/demo/event-01 接口,创建 3 次 Event 的监控数据。

CAT 监控数据 - Event - 分类列表

之后,点击 URL.Server 进行下钻,查看 URL.Server 分类 具体有哪些。如下图所示:

CAT 监控数据 - Event - 分类名字

至此我们可以发现,分类 type 是 Event 的一级分类,名字 name 是 Event 的二级分类,且二者是上下级关系。

2.6.2 logError 示例

在 DemoController 中,编写代码如下,编写 Event 的使用示例:

// DemoController.java

@GetMapping("/event-02")
public String event02() {
try {
int result = 1 / 0;
} catch (Throwable e) {
Cat.logError(e);
// Cat.logError("custom-message", e);
}
return "success";
}

调用 Cat 的 #logError(Throwable cause) 方法,记录一个带有错误堆栈信息的 Error。Error 是一种特殊的事件,它的 type 取决于传入的 Throwable e 异常。

  • 如果 e 是一个 Error 类型,type 会被设置为 Error
  • 如果 e 是一个 RuntimeException类型,type 会被设置为 RuntimeException
  • 其他情况下,type 会被设置为 Exception

同时,Throwable e 对应的异常类的全名 会设置到 Event 的 message 属性中。


下面,我们执行 Application 启动示例项目。启动完成后,调用 1 次 http://127.0.0.1:8080/demo/event-02 接口,创建 1 次 Event 的监控数据。

CAT 监控数据 - Event - 分类列表

之后,点击 RuntimeException 进行下钻,查看 RuntimeException 分类 具体有哪些。如下图所示:

CAT 监控数据 - Event - 分类名字

之后,点击「Log View」按钮,可以看到异常 Event 的具体堆栈信息。如下图所示:

CAT 监控数据 - Event - 分类名字 - 异常对堆栈

另外,我们在 CAT 控制台的 Program 报表中,可以看到该异常 Event。如下图所示:

CAT 监控数据 - Program

2.6.3 logErrorWithCategory 示例

在 DemoController 中,编写代码如下,编写 Event 的使用示例:

// DemoController.java

@GetMapping("/event-03")
public String event03() {
try {
int result = 1 / 0;
} catch (Throwable e) {
Cat.logErrorWithCategory("custom-category", e);
// Cat.logErrorWithCategory("custom-category", "custom-message", e);
}
return "success";
}

尽管 Event 的 name 默认会被设置为传入的 Throwable e 的类名,你仍然可以使用 Cat 的 #logErrorWithCategory(String category, Throwable cause) 方法,自定义异常 Event 的 namecategory 参数。


下面,我们执行 Application 启动示例项目。启动完成后,调用 1 次 http://127.0.0.1:8080/demo/event-03 接口,创建 1 次 Event 的监控数据。

CAT 监控数据 - Event - 分类列表

2.7 监控模型 Metric 示例

Metric 提供了两类 API,我们分成两个小节来瞅瞅。

2.7.1 次数 count 示例

在 DemoController 中,编写代码如下,编写 Metric 的次数使用示例:

// DemoController.java

@GetMapping("/metric-01")
public String metric01() {
Cat.logMetricForCount("visit.count", 1);
return "success";
}

调用 Cat 的 #logMetricForCount(String name, int quantity) 方法,记录一次次数类型的 Metric。

  • name 参数:Metric 名字。
  • quantity 参数:Metric 的次数。

注意,CAT 客户端每秒会聚合 Metric 的监控数据。如果我们在同一秒调用 count 三次(相同的 name 参数),CAT 客户端会累加他们的值,并且一次性上报给 CAT 服务端。


下面,我们执行 Application 启动示例项目。启动完成后,调用 3 次 http://127.0.0.1:8080/demo/metric-01 接口,创建 3 次 Metric 的监控数据。

耐心等待一会,我们就可以在 CAT 控制台看到名字为 visit.count 的 Metric 统计图。

CAT 监控数据 - Metric -

27.2 时长 duration 示例

在 DemoController 中,编写代码如下,编写 Metric 的时长使用示例:

// DemoController.java

@GetMapping("/metric-02")
public String metric02() {
Cat.logMetricForDuration("visit.duration", 10L);
return "success";
}

调用 Cat 的 #logMetricForDuration(String name, int durationInMillis) 方法,记录一次时长类型的 Metric。

  • name 参数:Metric 名字。
  • durationInMillis 参数:Metric 的时长,单位:毫秒。

注意,CAT 客户端每秒会聚合 Metric 的监控数据。如果我们在同一秒调用 duration 三次(相同的 name 参数),CAT 客户端会累加他们的值,并且一次性上报给 CAT 服务端。


下面,我们执行 Application 启动示例项目。启动完成后,调用 3 次 http://127.0.0.1:8080/demo/metric-02 接口,创建 3 次 Metric 的监控数据。

耐心等待一会,我们就可以在 CAT 控制台看到名字为 visit.duration 的 Metric 统计图。

CAT 监控数据 - Metric -

2.8 监控模型 Heartbeat 示例

监控模型 Heartbeat 的监控数据,并不需要我们手动调用 CAT 客户端 API 去收集,而是 CAT 客户端自动心跳上报 CPU 利用率、内存利用率、连接池状态、系统负载等等。

因此,我们可以直接在 CAT 控制台看到 Heartbeat 相关的监控报表。如下图所示:

CAT 监控数据 - Heartbeat

3. 日志集成

示例代码对应仓库:lab-61-logback

CAT 客户端针对日志组件进行集成,在我们使用 Logger 打印异常日志时,自动调用 CAT 客户端 API,创建监控模型 Event 的监控数据,上传到 CAT 服务端。

友情提示:通过这样的方式,我们无需改动项目的代码,自动将使用 logger.error(...) 集成到 CAT 监控平台。爽!

CAT 客户端目前提供了三种日志组件的集成,如下所示:

考虑到在 Spring Boot 应用中,我们使用 Logback 居多,所以就使用它进行演示。

下面,我们从「2. 快速入门」小节的 lab-61-demo 的基础上,复制出本小节的 lab-61-logback 示例项目。最终如下图所示:

项目结构

3.1 Logback 配置文件

创建 logback-spring.xml 配置文件,添加 Logback 配置文件。完整配置如下:

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

<!-- 参考 -->
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<include resource="org/springframework/boot/logging/logback/file-appender.xml" />

<!-- 定义 Sentry Appender -->
<appender name="CatAppender" class="com.dianping.cat.logback.CatLogbackAppender" />

<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
<appender-ref ref="CatAppender" />
</root>

</configuration>

重点是创建 CatLogbackAppender 目的地,实现在打印错误日志时,自动将该日志多上传到 CAT 服务器。

CatLogbackAppender 源码

3.2 LoggerController

创建 LoggerController 类,使用 Logger 打印异常日志。代码如下:

@RestController
@RequestMapping("/logger")
public class LoggerController {

private Logger logger = LoggerFactory.getLogger(getClass());

@GetMapping("/error")
public String error() {
try {
int result = 1 / 0;
} catch (Throwable e) {
// <X>
logger.error("计算异常", e);
}
return "success";
}

}

<X> 处的代码,在逻辑执行发生异常时,我们使用 Logger 打印 ERROR 级别的日志,而不是直接使用 CAT 客户端提供的 Event API。

3.3 简单测试

下面,我们执行 Application 启动示例项目。启动完成后,调用 1 次 http://127.0.0.1:8080/logger/error 接口,创建 1 次 Event 的监控数据。

CAT 监控数据 - Event - 分类列表

之后,点击 RuntimeException 进行下钻,查看 RuntimeException 分类 具体有哪些。如下图所示:

CAT 监控数据 - Event - 分类名字

之后,点击「Log View」按钮,可以看到异常 Event 的具体堆栈信息。如下图所示:

CAT 监控数据 - Event - 分类名字 - 异常对堆栈

4. SpringMVC 集成

示例代码对应仓库:lab-61-springmvc

CAT 客户端提供了对 SpringMVC 的集成,通过 CatFilter 过滤器实现。CatFilter 会过滤每一次请求,记录相应的 Transaction 和 Event 监控数据。

下面,我们从「2. 快速入门」小节的 lab-61-demo 的基础上,复制出本小节的 lab-61-springmvc 示例项目。最终如下图所示:

项目结构

4.1 CatFilterConfigure

创建 CatFilterConfigure 配置类,创建 CatFilter Bean。代码如下:

@Configuration
public class CatFilterConfigure {

@Bean
public FilterRegistrationBean<CatFilter> catFilter() {
// 创建 CatFilter 对象
CatFilter filter = new CatFilter();
// 创建 FilterRegistrationBean 对象
FilterRegistrationBean<CatFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(filter);
registration.addUrlPatterns("/*"); // 匹配所有 URL
registration.setName("cat-filter");
registration.setOrder(1);
return registration;
}

}

4.2 DemoController

创建 DemoController 类,提供测试用的示例 API。代码如下:

@RestController
@RequestMapping("/demo")
public class DemoController {

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

}

4.3 简单测试

下面,我们执行 Application 启动示例项目。启动完成后,调用 1 次 http://127.0.0.1:8080/demo/hello 接口,让 CatFilter 收集一次监控数据。

然后,我们来看看 CAT 控制台的监控数据。如下图所示:

CAT 监控数据 - Transaction - 分类列表

之后,点击 URL 进行下钻,查看 URL 分类 具体有哪些。如下图所示:

CAT 监控数据 - Transaction - 分类名字

之后,点击「Log View」按钮,查看整个 Transaction 的过程中所收集到的所有监控数据。如下图所示:

CAT 监控数据 - Transaction - Log View

友情提示:如果胖友使用了 SpringMVC REST 模式 URL 的话,参考 CAT 提供的 springmvc-url 集成方式,解决 URL 统计不准确的问题。

5. MySQL 集成

参考 CAT 官方推荐的集成方案,实现 SQL 操作的监控:

  • mybatis:基于 MyBatis ORM 框架
  • druid:基于 Druid 数据库连接池框架

6. Redis 集成

暂未找到合适的。

7. MongoDB 集成

暂未找到合适的。

8. Elasticsearch 集成

暂未找到合适的。

9. RocketMQ 集成

暂未找到合适的。

10. Kafka 集成

暂未找到合适的。

11. RabbitMQ 集成

暂未找到合适的。

12. ActiveMQ 集成

暂未找到合适的。

13. Dubbo 集成

参考 CAT 官方推荐的集成方案,实现 Dubbo 远程调用的监控:

14. Feign 集成

参考 CAT 官方推荐的集成方案,实现 Feign 远程调用的监控:

15. 注解支持

参考 CAT 官方推荐的集成方案,通过注解实现 CAT 针对指定方法的监控:

666. 彩蛋

至此,我们已经完成了 CAT 客户端接入的学习。

目前 CAT 提供的三方集成插件较少,这会导致我们需要自己开发一定的插件,进行 CAT 的集成。希望未来这块能够进行完善,以便 CAT 用户获得更好的 CAT 接入体验。

文章目录
  1. 1. 1. 概述
  2. 2. 2. 快速入门
    1. 2.1. 2.1 引入依赖
    2. 2.2. 2.2 配置文件
    3. 2.3. 2.3 环境配置
    4. 2.4. 2.4 Application
    5. 2.5. 2.5 监控模型 Transaction 示例
    6. 2.6. 2.6 监控模型 Event 示例
      1. 2.6.1. 2.6.1 logEvent 示例
      2. 2.6.2. 2.6.2 logError 示例
      3. 2.6.3. 2.6.3 logErrorWithCategory 示例
    7. 2.7. 2.7 监控模型 Metric 示例
      1. 2.7.1. 2.7.1 次数 count 示例
      2. 2.7.2. 27.2 时长 duration 示例
    8. 2.8. 2.8 监控模型 Heartbeat 示例
  3. 3. 3. 日志集成
    1. 3.1. 3.1 Logback 配置文件
    2. 3.2. 3.2 LoggerController
    3. 3.3. 3.3 简单测试
  4. 4. 4. SpringMVC 集成
    1. 4.1. 4.1 CatFilterConfigure
    2. 4.2. 4.2 DemoController
    3. 4.3. 4.3 简单测试
  5. 5. 5. MySQL 集成
  6. 6. 6. Redis 集成
  7. 7. 7. MongoDB 集成
  8. 8. 8. Elasticsearch 集成
  9. 9. 9. RocketMQ 集成
  10. 10. 10. Kafka 集成
  11. 11. 11. RabbitMQ 集成
  12. 12. 12. ActiveMQ 集成
  13. 13. 13. Dubbo 集成
  14. 14. 14. Feign 集成
  15. 15. 15. 注解支持
  16. 16. 666. 彩蛋