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

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


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

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

1. 概述

《Sentry 安装部署》文章中,我们已经对 Sentry 进行了学习,并完成了 Sentry 服务的搭建。如果还没看过的同学,需要先去学习一波。

友情提示:因为 Sentry 服务的搭建对网络要求比较高,具体原因你懂的,所以可以参考《Sentry 安装部署》文章的「4. Sentry 云服务」小节,先使用 Sentry 官方提供的云服务即可,还是有一定的免费额度的。

本文,我们来将 Java 项目接入 Sentry 服务中,实现将异常上传到 Sentry 服务,从而后续可以异常的展示和告警。Sentry 官方提供了 sentry-java 项目,方便我们进行接入。该项目如下图所示:sentry-java 项目

通过 sentry-java 项目,我们有两种方式将 Java 项目接入 Sentry 服务:

  • 方式一,通过针对日志库的拓展。在打印日志时,同时自动将错误日志上传到 Sentry 服务。
  • 方式二,通过 Sentry Java Client 提供的 API 方法,手动将错误信息上传到 Sentry 服务。

2. 方式一:日志库

示例项目:lab-51-sentry-logback

本小节,我们将通过 sentry-logback 项目,使用 Logback 打印日志,同时将错误日志自动上传到 Sentry 服务。

友情提示:这种方式,艿艿目前项目正在采用,可以快速将所有打印异常日志的地方,接入到 Sentry 服务。

新建 lab-51-sentry-logback 项目,最终如下图所示:项目结构

2.1 引入依赖

创建 pom.xml 文件,重点引入 sentry-logback 依赖。完整内容如下:

<?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>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>lab-51-sentry-logback</artifactId>

<dependencies>
<!-- 实现对 Spring MVC 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Sentry 对 Logback 的拓展,实现通过打日志的方式,来上传日志到 Sentry 服务 -->
<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry-logback</artifactId>
<version>1.7.30</version>
</dependency>
</dependencies>

</project>

2.2 Sentry 配置文件

创建 sentry.properties 配置文件,配置 Sentry 相关配置项。完整配置如下:

# Sentry 配置文件。每个配置 key,从 DefaultSentryClientFactory 类中寻找
# DSN
dsn=http://************@47.56.150.64:9000/3
# HTTP 请求建立连接超时时间
timeout=60000
# HTTP 请求等待结果超时时间
readtimeout=60000

所有配置项,对应 DefaultSentryClientFactory 类。

这里重点是设置 dsn 配置项,胖友可以到对应的 Sentry 项目中,进行 DSN 的获取。如下图所示:DSN

2.3 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="SentryAppender" class="io.sentry.logback.SentryAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
<!-- 使用默认的 SentryAppender encoder 格式 -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

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

</configuration>

重点是创建 SentryAppender 目的地,实现在打印错误日志时,自动将该日志多上传到 Sentry 服务。这里,我们设置 SentryAppender 只上传 WARN 级别以上的日志。

2.4 GlobalExceptionHandler

创建 GlobalExceptionHandler 类,全局异常处理器。代码如下:

@ControllerAdvice(basePackages = "cn.iocoder.springboot.lab51.sentrydemo.controller")
public class GlobalExceptionHandler {

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

// ... 省略其它类型异常的处理

/**
* 处理其它 Exception 异常
*/
@ResponseBody
@ExceptionHandler(value = Exception.class)
public String exceptionHandler(HttpServletRequest req, Exception e) {
// 记录异常日志
logger.error("[exceptionHandler]", e);
// 返回 ERROR CommonResult
return "系统异常";
}

}

这样,GlobalExceptionHandler 在捕捉到 Exception 异常时,打印错误日志,从而自动将该异常上传到 Sentry 服务中。

2.5 DemoController

创建 DemoController 类,提供两个示例 API。代码如下:

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

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

@GetMapping("/sentry")
public String sentry() {
logger.error("[main][我就是展示下异常!]");

return "success";
}

@GetMapping("/exception")
public String exception() {
throw new RuntimeException("直接抛出异常");
}

}

/demo/sentry 接口,使用 Logger 组件,打印异常日志。

/demo/exception 接口,抛出 RuntimeException 异常。主要目的是,测试「2.4 GlobalExceptionHandler」全局异常处理器。

2.6 DemoApplication

创建 DemoApplication 类,应用启动类。代码如下:

@SpringBootApplication
public class DemoApplication {

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

}

2.7 简单测试

使用 DemoApplication 启动项目。

① 使用浏览器,访问 http://127.0.0.1:8080/demo/sentry 地址,打印异常日志。

完成后,查看 Sentry 项目的 Issue 列表,可以看到该异常日志。如下图所示:Sentry Issue 列表

② 使用浏览器,访问 http://127.0.0.1:8080/demo/exception 地址,会抛出 RuntimeException 异常,而后被 GlobalExceptionHandler 处理,打印异常日志。

完成后,查看 Sentry 项目的 Issue 列表,可以看到该异常日志。如下图所示:Sentry Issue 列表

2.8 多环境配置

① 可能胖友希望收集正式环境的异常日志,可以参考《芋道 Spring Boot 日志集成 Logging 入门》文章的「7. Logback 扩展」小节的内容,通过 <springProfile /> 标签来实现不同环境的日志配置。例如说:

<!-- 省略其它内容 -->

<!-- 测试环境,独有的配置 -->
<springProfile name="dev">
<!-- 设置 Appender -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</springProfile>

<!-- 生产环境,独有的配置 -->
<springProfile name="prod">
<!-- 设置 Appender -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
<appender-ref ref="SentryAppender" /> <!-- 特立独行! -->
</root>
</springProfile>

② 如果测试环境的异常日志也收集到 Sentry 服务中,建议关闭 Sentry 在测试环境的告警通知,如下图所示:Sentry Alert Rule

至于如何设置 Environment 环境,有两种方式:

  • 方式一,参考《Sentry Integrations —— Logback》 文档,通过 Logback 提供的 MDC 机制,如下图所示:MDC 机制

  • 方式二,通过 sentry.properties 配置文件的 environment 配置项来设置。

    友情提示:不过这种方式,无法方便的实现相同项目代码的多环境设置,因此需要考虑 SENTRY_ENVIRONMENT 环境变量来实现。

    目前艿艿是自己写了 Sentry 的 Spring Boot Starter 库,和 Spring Profiles 机制做集成,通过在 Spring Boot 的 application-{profile}.yaml 配置文件中,设置不同的 sentry.environment 配置项来实现。

3. 方式二:Sentry Java Client

本小节,我们将通过 sentry 项目的 Sentry Java Client 提供的 API 方法,手动将错误信息上传到 Sentry 服务。

Sentry 官方为了我们方便集成 Spring Boot 项目中,提供了 sentry-spring-boot-starter 项目,贼良心。

新建 lab-51-sentry-spring 项目,最终如下图所示:项目结构

3.1 引入依赖

创建 pom.xml 文件,重点引入 sentry-spring-boot-starter 依赖,实现对 Sentry 的自动配置。完整内容如下:

<?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>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>lab-51-sentry-spring</artifactId>

<dependencies>
<!-- sentry-spring 的 Spring Boot Starter 库,实现它的自动配置 -->
<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry-spring-boot-starter</artifactId>
<version>1.7.30</version>
</dependency>
</dependencies>

</project>

3.2 配置文件

创建 application.yml 配置文件,配置 Sentry 相关配置项。完整配置如下:

# Sentry 配置项,对应 SentryProperties 配置类
sentry:
dsn: http://*****@47.56.150.64:9000/3 # DSN
timeout: 60000 # HTTP 请求建立连接超时时间

server:
port: 18080

其中,sentry 配置项,对应 SentryProperties 配置类,重点是设置 dsn 配置项。

3.3 SentryExceptionResolver

sentry-spring 项目提供了 SentryExceptionResolver 类,捕获 SpringMVC Controller 抛出的 Exception 异常,使用 Sentry Java Client 提供的 API 方法,手动将该异常上传到 Sentry 服务中。代码如下:

public class SentryExceptionResolver implements HandlerExceptionResolver, Ordered {

@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {

// <X> 上传异常
Sentry.capture(ex);

// null = run other HandlerExceptionResolvers to actually handle the exception
return null;
}

@Override
public int getOrder() {
// ensure this resolver runs first so that all exceptions are reported
return Integer.MIN_VALUE;
}

}

注意,SentryExceptionResolver 从功能上和「2.4 GlobalExceptionHandler」是一样的。所以,如果两者一起使用的情况下,会导致双倍快乐,上传了两份错误信息到 Sentry 服务中。

3.4 DemoController

创建 DemoController 类,提供两个示例 API。代码如下:

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

@Autowired
private SentryClient sentryClient;

@GetMapping("/sentry")
public String sentry() {
// 上传消息到 Sentry 中
sentryClient.sendMessage("示例消息");

// 上传异常到 Sentry 中
sentryClient.sendException(new RuntimeException("测试异常"));

// 上传事件到 Sentry 中
sentryClient.sendEvent(new EventBuilder().withMessage("示例事件").build());

return "success";
}

@GetMapping("/exception")
public String exception() {
throw new RuntimeException("直接抛出异常");
}

}

/demo/sentry 接口,使用 Sentry Java Client 提供的 API 方法,手动将该异常上传到 Sentry 服务中。这里,我们使用了 SentryClient 三种方法,上传了三条异常信息。

/demo/exception 接口,抛出 RuntimeException 异常。主要目的是,测试「3.3 SentryExceptionResolver」全局异常处理器。

3.5 DemoApplication

创建 DemoApplication 类,应用启动类。代码如下:

@SpringBootApplication
public class DemoApplication {

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

}

3.6 简单测试

使用 DemoApplication 启动项目。

① 使用浏览器,访问 http://127.0.0.1:18080/demo/sentry 地址,手动上传错误信息到 Sentry 服务中。

完成后,查看 Sentry 项目的 Issue 列表,可以看到该异常日志。如下图所示:Sentry Issue 列表

② 使用浏览器,访问 http://127.0.0.1:18080/demo/exception 地址,会抛出 RuntimeException 异常,而后被 GlobalExceptionHandler 处理,打印异常日志。

完成后,查看 Sentry 项目的 Issue 列表,可以看到该异常日志。如下图所示:Sentry Issue 列表

666. 彩蛋

至此,我们已经完成了 Java 项目如何接入 Sentry 服务之中。总的来说,通过日志库的方式,是最透明便捷的选择,无需对项目进行任何改造。

后续,胖友可以检查下是否很多“全局”的地方没有使用 Logger 打印异常日志。例如说:

  • SpringMVC 全局异常处理器

    友情提示:要剔除掉部分无用的异常哈,比如业务 ServieException,参数不正确 ConstraintViolationException 异常。

  • Job 全局异常处理器

  • @Async 异步的异常处理器

  • MQ 消费失败的异常处理器

  • ...

本文还缺少一些 Sentry 最佳实践的分享,后续艿艿继续补充,啊哈哈哈~

文章目录
  1. 1. 1. 概述
  2. 2. 2. 方式一:日志库
    1. 2.1. 2.1 引入依赖
    2. 2.2. 2.2 Sentry 配置文件
    3. 2.3. 2.3 Logback 配置文件
    4. 2.4. 2.4 GlobalExceptionHandler
    5. 2.5. 2.5 DemoController
    6. 2.6. 2.6 DemoApplication
    7. 2.7. 2.7 简单测试
    8. 2.8. 2.8 多环境配置
  3. 3. 3. 方式二:Sentry Java Client
    1. 3.1. 3.1 引入依赖
    2. 3.2. 3.2 配置文件
    3. 3.3. 3.3 SentryExceptionResolver
    4. 3.4. 3.4 DemoController
    5. 3.5. 3.5 DemoApplication
    6. 3.6. 3.6 简单测试
  4. 4. 666. 彩蛋