链路追踪

文章40 |   阅读 21585 |   点赞0

来源:https://blog.csdn.net/weixin_42073629/category_9940428.html

Spring Boot 监控平台 CAT 入门

x33g5p2x  于2021-12-21 转载在 其他  
字(12.2k)|赞(0)|评价(0)|浏览(637)

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 操作的监控:

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

相关文章