异常通知模块
# 异常通知模块
# 前言
项目线上难免会遇到各种问题,一个良好的及时异常通知机制,可以让我们在异常发生时及时捕获到关键信息,以针对性的进行问题排查。
# 使用方式
# 1.引入坐标
<dependency>
<groupId>cn.lovecyy</groupId>
<artifactId>relaxed-common-exception</artifactId>
<version>${version}</version>
</dependency>
# 2.配置切点拦截
默认切点拦截配置如下,仅仅拦截带
ExceptionNotice注解的类或方法
@Override
public Pointcut build() {
Pointcut cpc = new AnnotationMatchingPointcut(ExceptionNotice.class, true);
Pointcut mpc = new AnnotationMethodPoint(ExceptionNotice.class);
return new ComposablePointcut(cpc).union(mpc);
}
若要自定义拦截切点,需实现 PointCutRegister 重写build方法。
如下: 自定义切点 拦截RestController以及ExceptionNotice注解的
注: 嵌套注解 以线程为维度,只会触发一次异常通知(及统一交由最外层捕获)
@Component
public class CustomPointCut implements PointCutRegister {
@Override
public Pointcut build() {
Pointcut cpc = new AnnotationMatchingPointcut(ExceptionNotice.class, true);
Pointcut mpc = new AnnotationMethodPoint(ExceptionNotice.class);
Pointcut restMpc = new AnnotationMatchingPointcut(RestController.class);
return new ComposablePointcut(cpc).union(mpc).union(restMpc);
}
}
# 3.配置异常通知器
项目内置两个异常通知器1.邮件2.钉钉
# 邮件演示
# 1.引入邮件starter坐标
<dependency>
<groupId>cn.lovecyy</groupId>
<artifactId>relaxed-spring-boot-starter-mail</artifactId>
</dependency>
# 2.ExceptionHandleProperties异常处理属性
@Data
@ConfigurationProperties(prefix = "relaxed.exception")
public class ExceptionHandleProperties {
/**
* 通知渠道
*/
private Map<String, Boolean> channels = new HashMap<>();
/**
* 应用名称
*/
private String appName;
/**
* 忽略指定异常,请注意:只会忽略填写的异常类,而不会忽略该异常类的子类
*/
private Set<Class<? extends Throwable>> ignoreExceptions = new HashSet<>();
/**
* 通知间隔时间 单位秒 默认 5分钟
*/
private long time = TimeUnit.MINUTES.toSeconds(5);
/**
* 消息阈值 即便间隔时间没有到达设定的时间, 但是异常发生的数量达到阈值 则立即发送消息
*/
private int max = 5;
/**
* 堆栈转string 的长度
*/
private int length = 3000;
/**
* 接收异常通知邮件的邮箱
*/
private Set<String> receiveEmails = new HashSet<>();
}
# 3.注册自定义GlobalExceptionHandler的Bean
private static final String MAIL = "MAIL";
/**
* 邮件消息通知的日志处理器
*
* @author lingting 2020-06-12 00:32:40
*/
@Bean
public ExceptionNotifier mailGlobalExceptionNotifier(ExceptionHandleProperties exceptionHandleProperties,
ApplicationContext context) {
return new MailGlobalExceptionNotifier(MAIL, context.getBean(MailSender.class),
exceptionHandleProperties.getReceiveEmails());
}
# 4.配置application-web.yml
spring:
application:
name: test-web
mail:
host: smtp.163.com
port: 25
username: username@163.com
password: password
default-encoding: UTF-8
properties:
from: username@163.com
mail.smtp.auth: true
mail.smtp.starttls.enable: true
mail.smtp.starttls.required: true
mail.smtp.socketFactory.port: 465
mail.smtp.socketFactory.class: javax.net.ssl.SSLSocketFactory
mail.smtp.socketFactory.fallback: false
relaxed:
exception:
receiveEmails: receivename@vipsave.cn
max: 1
# 4.开始测试
# 1.创建AsyncService
@Component
public class AsyncService {
@ExceptionNotice
public void asyncError() {
if (true) {
throw new RuntimeException("异步任务处理出现异常");
}
}
}
# 2.创建TestService
@ExceptionNotice
@Component
public class TestService {
@Autowired
private TestAsyncService testAsyncService;
public String helloNoAnnotationReturn() {
new Thread(new Runnable() {
@Override
public void run() {
testAsyncService.asyncError();
}
}).start();
if (true) {
throw new RuntimeException("接口调用出现异常");
}
return "hello";
}
}
# 3.创建TestController
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private TestService testService;
@GetMapping("/hello")
public R test() {
testService.helloNoAnnotationReturn();
return R.ok();
}
}
4.测试方法
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("web")
@AutoConfigureMockMvc
public class WebRequestTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testWeb() throws Exception {
MvcResult mvcResult = null;
try {
mvcResult = mockMvc.perform(get("/test/hello")).andReturn();
}
catch (Exception e) {
//ignore
}
Thread.sleep(50000);
System.out.println(mvcResult);
}
}
# Spring boot Starter使用方式
# 1.引入坐标
<!--exception-->
<dependency>
<groupId>cn.lovecyy</groupId>
<artifactId>relaxed-spring-boot-starter-exception</artifactId>
<version>${revision}</version>
</dependency>
# 2.配置切点拦截
默认切点拦截配置如下,仅仅拦截带
ExceptionNotice注解的类或方法
@Override
public Pointcut build() {
Pointcut cpc = new AnnotationMatchingPointcut(ExceptionNotice.class, true);
Pointcut mpc = new AnnotationMethodPoint(ExceptionNotice.class);
return new ComposablePointcut(cpc).union(mpc);
}
若要自定义拦截切点,需实现 PointCutRegister 重写build方法。
如下: 自定义切点 拦截RestController以及ExceptionNotice注解的
注: 嵌套注解 以线程为维度,只会触发一次异常通知(及统一交由最外层捕获)
@Component
public class CustomPointCut implements PointCutRegister {
@Override
public Pointcut build() {
Pointcut cpc = new AnnotationMatchingPointcut(ExceptionNotice.class, true);
Pointcut mpc = new AnnotationMethodPoint(ExceptionNotice.class);
Pointcut restMpc = new AnnotationMatchingPointcut(RestController.class);
return new ComposablePointcut(cpc).union(mpc).union(restMpc);
}
}
# 3.配置异常通知器
项目内置两个异常通知器1.邮件2.钉钉.支持多渠道通知,若不配置则使用默认通知渠道
# 邮件演示
# 1.引入邮件starter坐标
<dependency>
<groupId>cn.lovecyy</groupId>
<artifactId>relaxed-spring-boot-starter-mail</artifactId>
</dependency>
# 2.application.yml
spring:
application:
name: test-web
mail:
host: smtp.163.com
port: 25
username: username@163.com
password: password
default-encoding: UTF-8
properties:
from: username@163.com
mail.smtp.auth: true
mail.smtp.starttls.enable: true
mail.smtp.starttls.required: true
mail.smtp.socketFactory.port: 465
mail.smtp.socketFactory.class: javax.net.ssl.SSLSocketFactory
mail.smtp.socketFactory.fallback: false
relaxed:
exception:
channels:
MAIL: true
#若需要使用钉钉
#DING_TALK: true
# 4.同上面的测试即可。
通知响应,默认有一个通知者成功,则为响应成功。
若需要自己决策成功响应条件,请实现NoticeResultDecision接口,并注册为Bean。
public class ExceptionNoticeResponse {
/**
* 是否成功 (任意一个成功即为成功)
*/
private boolean success;
/**
* 通知者数量
*/
private Integer notifierCount;
/**
* 单独的通知处理器结果
*/
List<ExceptionNoticeResult> noticeResults;
}