60.Spring Boot 监控平台 CAT 入门
60.Spring Boot 监控平台 CAT 入门
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
地址。
友情提示:可能胖友对
type
和name
参数有懵逼,稍后我们结合 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 控制台的监控数据。如下图所示:
之后,点击 URL 进行下钻,查看 URL 分类 具体有哪些。如下图所示:
至此我们可以发现,分类 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)
方法多次,添加的数据会被&
连接起来。 - 同时指定
duration
和durationStart
是没有意义的,尽管我们在样例中这样做了。 - 不要忘记标记 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
。
友情提示:可能胖友对
type
和name
参数有懵逼,稍后我们结合 CAT 控制台一起看看即可明白~😈 从 API 也可以看出,Transaction 和 Event 监控模型是比较接近的,相差就在时间属性上。
下面,我们执行 Application 启动示例项目。启动完成后,调用 3 次 http://127.0.0.1:8080/demo/event-01 接口,创建 3 次 Event 的监控数据。
之后,点击 URL.Server 进行下钻,查看 URL.Server 分类 具体有哪些。如下图所示:
至此我们可以发现,分类 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 的监控数据。
之后,点击 RuntimeException 进行下钻,查看 RuntimeException 分类 具体有哪些。如下图所示:
之后,点击「Log View」按钮,可以看到异常 Event 的具体堆栈信息。如下图所示:
另外,我们在 CAT 控制台的 Program 报表中,可以看到该异常 Event。如下图所示:
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 的 name
为 category
参数。
下面,我们执行 Application 启动示例项目。启动完成后,调用 1 次 http://127.0.0.1:8080/demo/event-03 接口,创建 1 次 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 统计图。
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 统计图。
2.8 监控模型 Heartbeat 示例
监控模型 Heartbeat 的监控数据,并不需要我们手动 调用 CAT 客户端 API 去收集,而是 CAT 客户端自动心跳上报 CPU 利用率、内存利用率、连接池状态、系统负载等等。
因此,我们可以直接在 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 服务器。
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 的监控数据。
之后,点击 RuntimeException 进行下钻,查看 RuntimeException 分类 具体有哪些。如下图所示:
之后,点击「Log View」按钮,可以看到异常 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 控制台的监控数据。如下图所示:
之后,点击 URL 进行下钻,查看 URL 分类 具体有哪些。如下图所示:
之后,点击「Log View」按钮,查看整个 Transaction 的过程中所收集到的所有监控数据。如下图所示:
友情提示:如果胖友使用了 SpringMVC REST 模式 URL 的话,参考 CAT 提供的 springmvc-url 集成方式,解决 URL 统计不准确的问题。
5. MySQL 集成
参考 CAT 官方推荐的集成方案,实现 SQL 操作的监控:
来源:https://blog.csdn.net/weixin_42073629/article/details/106870524