前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >prometheus-spring-boot-starter一个管理异常通知的神奇starter

prometheus-spring-boot-starter一个管理异常通知的神奇starter

作者头像
lyb-geek
发布2020-05-06 16:40:52
9860
发布2020-05-06 16:40:52
举报
文章被收录于专栏:Linyb极客之路Linyb极客之路

前言

对于工程的开发,必然会伴随着各种bug,工程量越大,出现bug的频率也会越高。一般对于代码量较小的工程来说,一个人可能就足够去做开发与维护;但是对于代码量较大的工程往往是需要一个小团队协作开发。当工程基本完成,开始部署测试环境或者生产环境时,这些环境并不能像开发环境一样能快速的调试与维护,线上的工程一旦出现异常时,开发团队就需要主动感知异常并协调处理,当然人不能一天24小时去盯着线上工程,所以就需要一种机制来自动化的对异常进行通知,并精确到谁负责的那块代码。这样会极大地方便后续的运维。因此,本项目的团队版上线

系统需求

JDK1.8+、maven3+、springboot2+

当前版本

0.4.1-personal

最快上手

  1. 将此工程通过mvn clean install打包到本地仓库中。
  2. 在你的工程中的pom.xml中做如下依赖
代码语言:javascript
复制
		<dependency>
			<groupId>com.kuding</groupId>
			<artifactId>prometheus-spring-boot-starter</artifactId>
			<version>0.4.1-personal</version>
		</dependency>
  1. application.properties或者application.yml中做如下的配置:(至于以上的配置说明后面的章节会讲到)
代码语言:javascript
复制
exceptionnotice:
  open-notice: true
  project-enviroment: develop
  included-trace-package:你的工程目录
  dingding:
    phone-num: 手机号(数组)
    web-hook: 钉钉的webhook
  1. 至于钉钉的配置请移步:钉钉机器人,这里需要特别说明一下,钉钉在2019年10月份(大概)对于钉钉机器人进行了一次改进,这次改进主要是安全设置变为必选项,原来已经配置过的钉钉机器人且没有配置安全设置的不受影响
  1. 以上配置好以后就可以写demo测试啦,首先创建第一个bean:
代码语言:javascript
复制
@Component
@ExceptionListener // 异常通知的监控来自这个注解
public class NoticeComponents {

	public void someMethod(String name) {
		System.out.println("这是一个参数:" + name);
		throw new NullPointerException("第一个异常");
	}

	public void anotherMethod(String name, int age) {
		System.out.println("这又是一个参数" + age);
		throw new IllegalArgumentException(name + ":" + age);
	}
}
  1. 以上都建立好了以后,就可以写单元测试了:
代码语言:javascript
复制
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {

	@Autowired
	private NoticeComponents noticeComponents;

	@Test
	public void contextLoads() {
		noticeComponents.someMethod("hello");
	}
}

当运行单元测试后,假如钉钉配置没有问题的话,你的钉钉中就会出现如下类似的消息:

假如在你配置的钉钉中出现类似这个信息的化,恭喜你,你成功的产生了一个异常通知。

综上,一个最简单的例子就完成了。

咋做的

本框架遵循spring boot starter的自动化配置规范而开发的自动化异常通知框架,在原有的单人版基础上进行了多处改进并升级成团队版,整体业务流程如下:

配置

本框架配置主要分为4部分:

  1. 全局配置
  2. 通知配置
  3. 策略配置
  4. 外援配置
全局配置
  • 下面是所有的全局配置(yml版):
代码语言:javascript
复制
exceptionnotice:
  open-notice: true
  project-name: XXX
  included-trace-package: com.kuding
  listen-type: common
  notice-type: dingding
  project-enviroment: develop
  dingding:
    phone-num: XXXX
    web-hook: https://oapi.dingtalk.com/robot/send?access_token=XXXX
    sign-secret: XXXX
    enable-signature-check: true
    dingding-text-type: markdown
  include-header-name:
  - name
  - tenantId
  exclude-exceptions:
  - java.lang.IllegalArgumentException
  strategy:
    frequency-type: showcount
    notice-show-count: 10
    enabled: true
  store:
    enable-redis-storage: false
    redis-key: XXX
  email:
    bcc:
    - XXX
    cc:
    - XXX
    email-text-type: text
    to:
    - XXX
  enable-async-notice: false
  async:
    thread-name-prefix: prometheus-task-
    core-pool-size: 1
    max-pool-size: 100
    daemon: false
    queue-capacity: 100
  • 具体说明如下:

名称

参数类型

说明

必要配置

全局配置

open-notice

boolean

用于开启异常通知(必填)

project-name

string

一般忽略,以spring.application.name替代

included-trace-package

string

异常追踪的包路径,一般情况下,此配置项就是配置你工程的包路径就可以了

listen-type

enum

监听类型,有两种:common/web-mvc,默认为common

notice-type

enum

通知类型,有两种:dingding/email

project-enviroment

enum

表示此工程环境,用于标注是哪个环境中的工程出异常

exclude-exceptions

list

排除异常,表示这些异常不需要进行异常通知

钉钉配置(dingding.)

phone-num

list

通知人的手机号(可以多个)

web-hook

string

钉钉机器人链接

enable-signature-check

boolean

是否开始验签验证

sign-secret

string

验签秘钥,开启验签验证后必填

dingding-text-type

enum

钉钉通知的文本类型:text/markdown,默认为text

邮件配置(email.)

bcc

list

秘密抄送人的邮箱

cc

list

抄送人的邮箱

to

list

发送人邮箱

email-text-type

enum

邮件通知的文本类型(只有text,没做扩展)

web-mvc限定

include-header-name

list

异常通知中需要包含的header信息(不写全部返回)

异步配置(async.)

enbaled

boolean

是否开启异步通知(默认否)

thread-name-prefix

string

线程名称前缀

core-pool-size

int

核心池数量

max-pool-size

int

最大线程池数量

daemon

boolean

是否需要守护线程

策略配置(strategy)

enabled

boolean

是否开启通知策略(默认否)

frequency-type

enum

通知频率策略类型:showcount/timeout

notice-show-count

int

当策略类型为showcount时,表示距上次通知再出钱多少次需要再次通知

notice-time-interval

duration

当策略类型为timeout是,表示距上次通知经过多长时间后再次通知

通知存储(store.)

enable-redis-storage

boolean

是否开启redis存储(默认否)

redis-key

string

(开启redis限定)redis存储的键值

  • 以上通知中最重要的当属exceptionnotice.listen-type,此配置表示工程的监听方式,目前有两种监听方式:普通监听(common);mvc监听(web-mvc) 。这两种监听方式各有千秋,普通监听方式主要运用aop的方式对有注解的方法或类进行监听,可以加在任何类与方法上。mvc监听只能对controller层进行监听,对其它层无效,不过异常通知的信息更丰富,不仅仅包括了普通监听的所有信息(不包含参数),还包含了请求中的路径信息(path)、参数信息(param)、请求中的请求体信息(body)和请求体中的头信息(header)。例如:
代码语言:javascript
复制
@RestController
@RequestMapping("/demo")
public class DemoController {

	@Autowired
	private DingdingNoticeService dingdingNoticeService;

	@Autowired
	private CustomService customService;

	@PostMapping("/dingding1/{pathParam}")
	@ExceptionListener
	public String dingding1(@PathVariable @ApiParam(value = "来个路径参数", required = true) String pathParam,
			@RequestParam @ApiParam(value = "来个参数", required = true) String param,
			@RequestHeader @ApiParam(value = "来个请求头", required = true) String headParam,
			@RequestBody @ApiParam(value = "来个请求体", required = true) String body) {
		dingdingNoticeService.dinding1();
		return "Keep on going never give up.";
	}
	...more code

具体效果如下:

  • 实际上钉钉机器人提供了很多其他的消息类型,这里适配了钉钉的markdown文本输出,只需要配置exceptionnotice.dingding-text-type=markdown即可,最终你可以得到以下面类似的效果:
  • 项目中的异常一般分类两大类:第一类为未捕获异常,第二类为业务异常。业务异常一般由用户自己定义的异常,在javaweb项目中,假如用户的请求不满足返回结果的条件,一般是需要主动抛出自定义异常的,所以这类异常并不需要进行通知。排除不需要异常通知的配置如下:
代码语言:javascript
复制
 exceptionnotice.exclude-exceptions=java.lang.IllegalArgumentException,com.yourpackage.YourLogicException
  • 为了方便进行扩展开发,本框架还支持异常信息的持久化,目前只支持进行redis的持久化,开启redis持久化需要进行如下配置
代码语言:javascript
复制
exceptionnotice.enable-redis-storage=true
exceptionnotice.redis-key=你自己的redis键

这里开启redis存储需要依赖spring-boot-starter-data-redis,需要用户自行配置,不过目前redis的存储不建议使用,后续我会完善异常持久化的流程

  • 上一版本的异常通知由于是同步的,由于各种各样的原因,在异常通知失败时,框架本身可能也会产生一些异常,所以增加了异步通知,主要配置为exceptionnotice.async.enabled=true,开启异常配置需要配置一个自有的线程池,具体配置如下:
代码语言:javascript
复制
exceptionnotice:
  async:
    core-pool-size: 1
    max-pool-size: 100
    daemon: false
    queue-capacity: 50
    thread-name-prefix: notice-

以上配置实际上对应着spring中的ThreadPoolTaskExecutor一部分配置,配置的东西也比较简单,要是有具体的其他特殊的线程配置请留言提建议

通知配置
  • 通知配置一共三总类型:
    1. 钉钉通知
    2. 邮件通知
    3. 自定义通知
  • 钉钉通知配置:
代码语言:javascript
复制
exceptionnotice:
  dingding:
    phone-num: 手机号(数组)
    web-hook: 钉钉的web钩子
    enable-signature-check: 是否开启签名验证(true/false)
    sign-secret: 签名秘钥

   ...........

开启签名验证时,需要在钉钉机器人的安全设置中选中加签设置:

  • 邮件通知同样也延续了原来的邮件配置,同样依赖spring-boot-starter-mail及其配置
代码语言:javascript
复制
spring:
  mail:
    host: smtp.xxx.com
    port: 25
    username: 开启smtp权限的邮箱用户名
    password: 密码
exceptionnotice:
  email:
    to: 给谁发
    cc: 抄送给谁
    bcc: 秘密抄送给谁
  • 框架内保留了通知所需的所有的接口,使用者可以自行定义自己的通知方式,详情请先往下看,后面有介绍
策略配置

一般而言,一个方法出现异常信息,意味着每当同样的方式进行调用时都会抛出相同的异常方法,放任不管的话,钉钉异常通知与邮件异常通知会重复的收到同一个异常,所以为了限制发送频率,默认情况下,某个方法出现的异常需要通知,那么这条通知每天只会出现一次。当然这样也可能会出现问题,假如邮件或钉钉信息没有收到,很可能会漏掉此通知,所以这里创造了一个通知的策略。

  • 通知策略对应的配置类为ExceptionNoticeFrequencyStrategy,开启策略需要在application.properties中加入如下配置
代码语言:javascript
复制
exceptionnotice.strategy.enabled=true
  • 目前一共有两种通知策略
    • 时间策略
    • 出现频次策略

对应的配置为:

代码语言:javascript
复制
exceptionnotice.strategy.frequency-type=timeout/showcount
  • 时间策略对应的配置项为exceptionnotice.strategy.notice-time-interval,类型为duration,表示的是自上次通知时间起,超过了此配置的时间后,便会再次通知。
  • 频次策略对应的配置项为exceptionnotice.strategy.notice-show-count,表示的是自上次通知起,出现次数超过了此配置测次数后,便会再次通知

基本上以上策略足够使用,假如说还有更好的配置策略可以连系我

外援配置

目前需要外援配置的信息有以下几个,其实上面也提到了

  1. 开启redis存储的话需要在pom.xml中加入如下依赖
代码语言:javascript
复制
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>

加入依赖后需要开始配置redis

代码语言:javascript
复制
spring:
  redis:
    host: 127.0.0.1
    port: 6379
    database: 0
    password: 密码
  1. 有邮件通知的话需要在pom.xml中加入如下依赖
代码语言:javascript
复制
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-mail</artifactId>
		</dependency>

加入依赖后开始配置邮件信息

代码语言:javascript
复制
spring:
  mail:
    host: smtp.xxx.com
    port: 25
    username: 开启smtp权限的邮箱用户名
    password: 密码
  1. 开启web-mvc模式的话,不说也知道,一定会引入如下依赖:
代码语言:javascript
复制
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

注解

  • 上面讲的配置实际上是为此注解服务的,框架内唯一的注解@ExceptionListener,需要注意的是,凡是你需要异常通知的类或方法上必须加此注解。
  • 根据配置类型exceptionnotice.listen-type的不同配置注解的位置也是不一样的
    • exceptionnotice.listen-type=common@ExceptionListener可以加到任意类上,任意方法上
    • exceptionnotice.listen-type=web-mvc时,@ExceptionListener只能加在Controller层即带有@Controller@RestController的类或者方法上,方法上也需要有对应的@RequestMapping相关的注解

自定义Config

  • 目前在上面所有的配置的基础上,本框架还加入了更多的扩展配置,主要包含以下几个模块:
    1. 自定义的消息通知组件
    2. 自定义的异常通知文本结构
    3. 自定义的http组件
  • 自定义消息组件主要由接口INoticeSendComponent进行消息发送:
代码语言:javascript
复制
public interface INoticeSendComponent {
	public void send(String blamedFor, ExceptionNotice exceptionNotice);
	public Collection<String> getAllBuddies();
}

其中主要的方法为public void send(String blamedFor, ExceptionNotice exceptionNotice)表示为我的异常通知(ExceptionNotice)需要发给谁(blameFor)。public Collection<String> getAllBuddies()表示那些buddy需要用这个消息通知组件进行消息发送:

代码语言:javascript
复制
@Component
public class MySendExceptionComponent implements INoticeSendComponent {
	
	private List<String> allBoddies = Arrays.asList("Tom", "Jerry");
	
	private final Log logger = LogFactory.getLog(getClass());
	
	@Override
	public void send(String blamedFor, ExceptionNotice exceptionNotice) {
		logger.error(blamedFor + "-->" + exceptionNotice.getExceptionMessage());
	}
	
	@Override
	public Collection<String> getAllBuddies() {
		return allBoddies;
	}
}

配置好以后需要加入到现有的配置体系中,所以需要增加一个spring的config配置并实现ExceptionSendComponentConfigure接口:

代码语言:javascript
复制
@Configuration
public class CustomSendComponentConfig implements ExceptionSendComponentConfigure {

	@Autowired
	private MySendExceptionComponent mySendExceptionComponent;

	@Override
	public void addSendComponent(ExceptionHandler exceptionHandler) {
		exceptionHandler.registerNoticeSendComponent(mySendExceptionComponent);
	}

}

这样,自定义的消息通知系统就可以通过@ExceptionListener注解来使用了

代码语言:javascript
复制
	@PutMapping("/custom")
	@ExceptionListener("Tom")
	public String custom() {
		customService.custom();
		return "Whatever is worth doing is worth doing well.";
	}
//2020-01-03 20:30:46.480 ERROR 1576 --- [ometheus-task-1] c.h.components.MySendExceptionComponent  : Tom-->[java.lang.NullPointerException:自定义通知第一个]
  • 自定义异常通知文本接口主要是用户可以自行编辑所需要的文本结构进行通知,控制输出异常通知的文本是由ExceptionNoticeResolverFactory进行管理的,通过管理ExceptionNoticeResolver来输出不同的文本。所以需要自己的异常消息配置需要实现实现ExceptionNoticeResolver接口:
代码语言:javascript
复制
@Component
public class MyExceptionNoticeResolver implements ExceptionNoticeResolver {

	@Override
	public String resolve(ExceptionNotice exceptionNotice) {
		StringBuilder builder = new StringBuilder();
		builder.append("人非圣贤,孰能无错?").append("\n");
		exceptionNotice.getExceptionMessage().forEach(x -> builder.append(x).append("\n"));
		return builder.toString();
	}
}

然后将此bean交由ExceptionNoticeResolverFactory进行管理,可以通过spring的config配置并实现ExceptionNoticeResolverConfigure接口

代码语言:javascript
复制
@Configuration
public class MyExceptionTextResolverConfig implements ExceptionNoticeResolverConfigure {

	@Autowired
	private MyExceptionNoticeResolver myExceptionNoticeResolver;

	@Override
	public void addResolver(ExceptionNoticeResolverFactory factory) {
		factory.addNoticeResolver("dingding", myExceptionNoticeResolver);
	}
}

这里需要说明一下,ExceptionNoticeResolverFactory中的方法public void addNoticeResolver(String resolveKey, ExceptionNoticeResolver resolver)resolveKey表示的对ExceptionNoticeResolver对象的唯一标识。框架提供的dingding通知与email通知有其默认的解析标识,分别为dingdingemail,所以假如你有特殊的钉钉通知或email通知需要,可以通过上述的方式进行自定义。

有些需要注意的地方

1、对于配置exceptionnotice.listen-type=web-mvc,请注意是“web-mvc”,不是“web_mvc”,也不是“WEB_MVC”我发现很多网友这块配置老出问题
2、0.5.1-team这个版本默认需要依赖spring-boot-starter-web,所以在工程中配置此框架必须要引入spring-boot-starter-web,我这块还没处理好,所以对于非web项目目前请自行改造

作者:donald2008abc 来源:https://gitee.com/ITEater/prometheus-spring-boot-starter

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Linyb极客之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 系统需求
  • 当前版本
  • 最快上手
  • 咋做的
    • 配置
      • 全局配置
      • 通知配置
      • 策略配置
      • 外援配置
    • 注解
      • 自定义Config
        • 有些需要注意的地方
          • 1、对于配置exceptionnotice.listen-type=web-mvc,请注意是“web-mvc”,不是“web_mvc”,也不是“WEB_MVC”我发现很多网友这块配置老出问题
          • 2、0.5.1-team这个版本默认需要依赖spring-boot-starter-web,所以在工程中配置此框架必须要引入spring-boot-starter-web,我这块还没处理好,所以对于非web项目目前请自行改造
      相关产品与服务
      云数据库 Redis
      腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档