利用AOP實(shí)現(xiàn)系統(tǒng)告警的方法詳解
一、業(yè)務(wù)背景
在開發(fā)的過程中會(huì)遇到各種各樣的開發(fā)問題,服務(wù)器宕機(jī)、網(wǎng)絡(luò)抖動(dòng)、代碼本身的bug等等。針對(duì)代碼的bug,我們可以提前預(yù)支,通過發(fā)送告警信息來警示我們?nèi)ジ深A(yù),盡早處理。
二、告警的方式
1、釘釘告警
通過在企業(yè)釘釘群,添加群機(jī)器人的方式,通過機(jī)器人向群內(nèi)發(fā)送報(bào)警信息。至于釘釘機(jī)器人怎么創(chuàng)建,發(fā)送消息的api等等,請(qǐng)參考官方文檔
2、企業(yè)微信告警
同樣的套路,企業(yè)微信也是,在企業(yè)微信群中,添加群機(jī)器人。通過機(jī)器人發(fā)送告警信息。具體請(qǐng)看官方文檔
3、郵件告警
與上述不同的是,郵件是發(fā)送給個(gè)人的,當(dāng)然也可以是批量發(fā)送,只實(shí)現(xiàn)了發(fā)送文本格式的方式,至于markdown格式,有待考察。郵件發(fā)送相對(duì)比較簡(jiǎn)單,這里就不展開贅述。
三、源碼解析
1、Alarm自定義注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public?@interface?Alarm?{
????/**
?????*?報(bào)警標(biāo)題
?????*
?????*?@return?String
?????*/
????String?title()?default?"";
????/**
?????*?發(fā)送報(bào)警格式:目前支持text,markdown
?????*?@return
?????*/
????MessageTye?messageType()?default?MessageTye.TEXT;
????/**
?????*?告警模板id
?????*?@return
?????*/
????String?templateId()?default?"";
????/**
?????*?成功是否通知:true-通知,false-不通知
?????*?@return
?????*/
????boolean?successNotice()?default?false;
}
1.1、注解使用
@Alarm標(biāo)記在方法上使用,被標(biāo)記的方法發(fā)生異常,會(huì)根據(jù)配置,讀取配置信息,發(fā)送異常堆棧信息。使用方法如下所示:
@Alarm(title?=?"某某業(yè)務(wù)告警",?messageType?=?MessageTye.MARKDOWN,?templateId?=?"errorTemp")
1.2、注解字段解析
title
告警消息標(biāo)題:可以定義為業(yè)務(wù)信息,如導(dǎo)師身份計(jì)算
messageType
告警消息展示類型:目前支持text文本類型,markdown類型
templateId
消息模板id:與配置文件中配置的模板id一致
successNotice
正常情況是否也需要發(fā)送告警信息,默認(rèn)值是fasle,表示不需要發(fā)送。當(dāng)然,有些業(yè)務(wù)場(chǎng)景正常情況也需要發(fā)送,比如:支付出單通知等。
2、配置文件分析
2.1、釘釘配置文件
spring: ??alarm: ????dingtalk: ?????#?開啟釘釘發(fā)送告警 ??????enabled:?true ?????#?釘釘群機(jī)器人唯一的token ??????token:?xxxxxx ?????#?安全設(shè)置:加簽的密鑰 ??????secret:?xxxxxxx
2.2、企業(yè)微信配置文件
spring: ??alarm: ????wechat: ?????#?開啟企業(yè)微信告警 ??????enabled:?true ?????#?企業(yè)微信群機(jī)器人唯一key ??????key:?xxxxxdsf ?????#?被@人的手機(jī)號(hào) ??????to-user:?1314243
2.3、郵件配置文件
spring: ??alarm:???? ????mail: ??????enabled:?true ??????smtpHost:?xxx@qq.com ??????smtpPort:?22 ??????to:?xxx@qq.com ??????from:?132@qq.com ??????username:?wsrf ??????password:?xxx
2.4、自定義模板配置
spring: ??alarm: ????template: ??????#?開啟通過模板配置 ??????enabled:?true ??????#?配置模板來源為文件 ??????source:?FILE ??????#?配置模板數(shù)據(jù) ??????templates: ????????errorTemp: ??????????templateId:?errorTemp ??????????templateName:?服務(wù)異常模板 ??????????templateContent:?這里是配置模板的內(nèi)容
spring:alarm:template:enabled,Boolean類型,表示開啟告警消息使用模板發(fā)送。spring:alarm:template:source,模板來源,枚舉類:JDBC(數(shù)據(jù)庫)、FILE(配置文件)、MEMORY(內(nèi)存),目前只支持FILE,其他兩種可自行擴(kuò)展。spring:alarm:template:templates,配置模板內(nèi)容,是一個(gè)map,errorTemp是模板id,需要使用哪種模板,就在@Alarm中的templateId設(shè)置為對(duì)應(yīng)配置文件中的templateId。
3、核心AOP分析
3.1、原理分析

3.2、自定義切面
@Aspect
@Slf4j
@RequiredArgsConstructor
public?class?AlarmAspect?{
????private?final?AlarmTemplateProvider?alarmTemplateProvider;
????private?final?static?String?ERROR_TEMPLATE?=?"\n\n<font?color=\"#F37335\">異常信息:</font>\n"?+
????????????"```java\n"?+
????????????"#{[exception]}\n"?+
????????????"```\n";
????private?final?static?String?TEXT_ERROR_TEMPLATE?=?"\n異常信息:\n"?+
????????????"#{[exception]}";
????private?final?static?String?MARKDOWN_TITLE_TEMPLATE?=?"#?【#{[title]}】\n"?+
????????????"\n請(qǐng)求狀態(tài):<font color=\"#{[stateColor]}\">#{[state]}</font>\n\n";
????private?final?static?String?TEXT_TITLE_TEMPLATE?=?"【#{[title]}】\n"?+
????????????"請(qǐng)求狀態(tài):#{[state]}\n";
????@Pointcut("@annotation(alarm)")
????public?void?alarmPointcut(Alarm?alarm)?{
????}
????@Around(value?=?"alarmPointcut(alarm)",?argNames?=?"joinPoint,alarm")
????public?Object?around(ProceedingJoinPoint?joinPoint,?Alarm?alarm)?throws?Throwable?{
????????Object?result?=?joinPoint.proceed();
????????if?(alarm.successNotice())?{
????????????String?templateId?=?alarm.templateId();
????????????String?fileTemplateContent?=?"";
????????????if?(Objects.nonNull(alarmTemplateProvider))?{
????????????????AlarmTemplate?alarmTemplate?=?alarmTemplateProvider.loadingAlarmTemplate(templateId);
????????????????fileTemplateContent?=?alarmTemplate.getTemplateContent();
????????????}
????????????String?templateContent?=?"";
????????????MessageTye?messageTye?=?alarm.messageType();
????????????if?(messageTye.equals(MessageTye.TEXT))?{
????????????????templateContent?=?TEXT_TITLE_TEMPLATE.concat(fileTemplateContent);
????????????}?else?if?(messageTye.equals(MessageTye.MARKDOWN))?{
????????????????templateContent?=?MARKDOWN_TITLE_TEMPLATE.concat(fileTemplateContent);
????????????}
????????????Map<String,?Object>?alarmParamMap?=?new?HashMap<>();
????????????alarmParamMap.put("title",?alarm.title());
????????????alarmParamMap.put("stateColor",?"#45B649");
????????????alarmParamMap.put("state",?"成功");
????????????sendAlarm(alarm,?templateContent,?alarmParamMap);
????????}
????????return?result;
????}
????@AfterThrowing(pointcut?=?"alarmPointcut(alarm)",?argNames?=?"joinPoint,alarm,e",?throwing?=?"e")
????public?void?doAfterThrow(JoinPoint?joinPoint,?Alarm?alarm,?Exception?e)?{
????????log.info("請(qǐng)求接口發(fā)生異常?:?[{}]",?e.getMessage());
????????String?templateId?=?alarm.templateId();
????????//?加載模板中配置的內(nèi)容,若有
????????String?templateContent?=?"";
????????String?fileTemplateContent?=?"";
????????if?(Objects.nonNull(alarmTemplateProvider))?{
????????????AlarmTemplate?alarmTemplate?=?alarmTemplateProvider.loadingAlarmTemplate(templateId);
????????????fileTemplateContent?=?alarmTemplate.getTemplateContent();
????????}
????????MessageTye?messageTye?=?alarm.messageType();
????????if?(messageTye.equals(MessageTye.TEXT))?{
????????????templateContent?=?TEXT_TITLE_TEMPLATE.concat(fileTemplateContent).concat(TEXT_ERROR_TEMPLATE);
????????}?else?if?(messageTye.equals(MessageTye.MARKDOWN))?{
????????????templateContent?=?MARKDOWN_TITLE_TEMPLATE.concat(fileTemplateContent).concat(ERROR_TEMPLATE);
????????}
????????Map<String,?Object>?alarmParamMap?=?new?HashMap<>();
????????alarmParamMap.put("title",?alarm.title());
????????alarmParamMap.put("stateColor",?"#FF4B2B");
????????alarmParamMap.put("state",?"失敗");
????????alarmParamMap.put("exception",?ExceptionUtil.stacktraceToString(e));
????????sendAlarm(alarm,?templateContent,?alarmParamMap);
????}
????private?void?sendAlarm(Alarm?alarm,?String?templateContent,?Map<String,?Object>?alarmParamMap)?{
????????ExpressionParser?parser?=?new?SpelExpressionParser();
????????TemplateParserContext?parserContext?=?new?TemplateParserContext();
????????String?message?=?parser.parseExpression(templateContent,?parserContext).getValue(alarmParamMap,?String.class);
????????MessageTye?messageTye?=?alarm.messageType();
????????NotifyMessage?notifyMessage?=?new?NotifyMessage();
????????notifyMessage.setTitle(alarm.title());
????????notifyMessage.setMessageTye(messageTye);
????????notifyMessage.setMessage(message);
????????AlarmFactoryExecute.execute(notifyMessage);
????}
}
4、模板提供器
4.1、AlarmTemplateProvider
定義一個(gè)抽象接口AlarmTemplateProvider,用于被具體的子類實(shí)現(xiàn)
public?interface?AlarmTemplateProvider?{
????/**
?????*?加載告警模板
?????*
?????*?@param?templateId?模板id
?????*?@return?AlarmTemplate
?????*/
????AlarmTemplate?loadingAlarmTemplate(String?templateId);
}
4.2、BaseAlarmTemplateProvider
抽象類BaseAlarmTemplateProvider實(shí)現(xiàn)該抽象接口
public?abstract?class?BaseAlarmTemplateProvider?implements?AlarmTemplateProvider?{
????@Override
????public?AlarmTemplate?loadingAlarmTemplate(String?templateId)?{
????????if?(StringUtils.isEmpty(templateId))?{
????????????throw?new?AlarmException(400,?"告警模板配置id不能為空");
????????}
????????return?getAlarmTemplate(templateId);
????}
????/**
?????*?查詢告警模板
?????*
?????*?@param?templateId?模板id
?????*?@return?AlarmTemplate
?????*/
????abstract?AlarmTemplate?getAlarmTemplate(String?templateId);
}
4.3、YamlAlarmTemplateProvider
具體實(shí)現(xiàn)類YamlAlarmTemplateProvider,實(shí)現(xiàn)從配置文件中讀取模板,該類在項(xiàng)目啟動(dòng)時(shí),會(huì)被加載進(jìn)spring的bean容器
@RequiredArgsConstructor
public?class?YamlAlarmTemplateProvider?extends?BaseAlarmTemplateProvider?{
????private?final?TemplateConfig?templateConfig;
????@Override
????AlarmTemplate?getAlarmTemplate(String?templateId)?{
????????Map<String,?AlarmTemplate>?configTemplates?=?templateConfig.getTemplates();
????????AlarmTemplate?alarmTemplate?=?configTemplates.get(templateId);
????????if?(ObjectUtils.isEmpty(alarmTemplate))?{
????????????throw?new?AlarmException(400,?"未發(fā)現(xiàn)告警配置模板");
????????}
????????return?alarmTemplate;
????}
}
4.4、MemoryAlarmTemplateProvider和JdbcAlarmTemplateProvider
抽象類BaseAlarmTemplateProvider還有其他兩個(gè)子類,分別是MemoryAlarmTemplateProvider和JdbcAlarmTemplateProvider。但是這兩個(gè)子類暫時(shí)還未實(shí)現(xiàn)邏輯,后續(xù)可以自行擴(kuò)展。
@RequiredArgsConstructor
public?class?MemoryAlarmTemplateProvider?extends?BaseAlarmTemplateProvider?{
????private?final?Function<String,?AlarmTemplate>?function;
????@Override
????AlarmTemplate?getAlarmTemplate(String?templateId)?{
????????AlarmTemplate?alarmTemplate?=?function.apply(templateId);
????????if?(ObjectUtils.isEmpty(alarmTemplate))?{
????????????throw?new?AlarmException(400,?"未發(fā)現(xiàn)告警配置模板");
????????}
????????return?alarmTemplate;
????}
}
@RequiredArgsConstructor
public?class?JdbcAlarmTemplateProvider?extends?BaseAlarmTemplateProvider?{
????private?final?Function<String,?AlarmTemplate>?function;
????@Override
????AlarmTemplate?getAlarmTemplate(String?templateId)?{
????????AlarmTemplate?alarmTemplate?=?function.apply(templateId);
????????if?(ObjectUtils.isEmpty(alarmTemplate))?{
????????????throw?new?AlarmException(400,?"未發(fā)現(xiàn)告警配置模板");
????????}
????????return?alarmTemplate;
????}
}
兩個(gè)類中都有Function<String, AlarmTemplate>接口,為函數(shù)式接口,可以供外部自行去實(shí)現(xiàn)邏輯。
5、告警發(fā)送
5.1、AlarmFactoryExecute
該類內(nèi)部保存了一個(gè)容器,主要用于緩存真正的發(fā)送類
public?class?AlarmFactoryExecute?{
????private?static?List<AlarmWarnService>?serviceList?=?new?ArrayList<>();
????public?AlarmFactoryExecute(List<AlarmWarnService>?alarmLogWarnServices)?{
????????serviceList?=?alarmLogWarnServices;
????}
????public?static?void?addAlarmLogWarnService(AlarmWarnService?alarmLogWarnService)?{
????????serviceList.add(alarmLogWarnService);
????}
????public?static?List<AlarmWarnService>?getServiceList()?{
????????return?serviceList;
????}
????public?static?void?execute(NotifyMessage?notifyMessage)?{
????????for?(AlarmWarnService?alarmWarnService?:?getServiceList())?{
????????????alarmWarnService.send(notifyMessage);
????????}
????}
}
5.2、AlarmWarnService
抽象接口,只提供一個(gè)發(fā)送的方法
public?interface?AlarmWarnService?{
????/**
?????*?發(fā)送信息
?????*
?????*?@param?notifyMessage?message
?????*/
????void?send(NotifyMessage?notifyMessage);
}
5.3、BaseWarnService
與抽象的模板提供器AlarmTemplateProvider一樣的套路,該接口有一個(gè)抽象的實(shí)現(xiàn)類BaseWarnService,該類對(duì)外暴露send方法,用于發(fā)送消息,內(nèi)部用doSendMarkdown,doSendText方法實(shí)現(xiàn)具體的發(fā)送邏輯,當(dāng)然具體發(fā)送邏輯還是得由其子類去實(shí)現(xiàn)。
@Slf4j
public?abstract?class?BaseWarnService?implements?AlarmWarnService?{
????@Override
????public?void?send(NotifyMessage?notifyMessage)?{
????????if?(notifyMessage.getMessageTye().equals(MessageTye.TEXT))?{
????????????CompletableFuture.runAsync(()?->?{
????????????????try?{
????????????????????doSendText(notifyMessage.getMessage());
????????????????}?catch?(Exception?e)?{
????????????????????log.error("send?text?warn?message?error",?e);
????????????????}
????????????});
????????}?else?if?(notifyMessage.getMessageTye().equals(MessageTye.MARKDOWN))?{
????????????CompletableFuture.runAsync(()?->?{
????????????????try?{
????????????????????doSendMarkdown(notifyMessage.getTitle(),?notifyMessage.getMessage());
????????????????}?catch?(Exception?e)?{
????????????????????log.error("send?markdown?warn?message?error",?e);
????????????????}
????????????});
????????}
?}
????/**
?????*?發(fā)送Markdown消息
?????*
?????*?@param?title???Markdown標(biāo)題
?????*?@param?message?Markdown消息
?????*?@throws?Exception?異常
?????*/
????protected?abstract?void?doSendMarkdown(String?title,?String?message)?throws?Exception;
????/**
?????*?發(fā)送文本消息
?????*
?????*?@param?message?文本消息
?????*?@throws?Exception?異常
?????*/
????protected?abstract?void?doSendText(String?message)?throws?Exception;
}
5.4、DingTalkWarnService
主要實(shí)現(xiàn)了釘釘發(fā)送告警信息的邏輯
@Slf4j
public?class?DingTalkWarnService?extends?BaseWarnService?{
????private?static?final?String?ROBOT_SEND_URL?=?"https://oapi.dingtalk.com/robot/send?access_token=";
????private?final?String?token;
????private?final?String?secret;
????public?DingTalkWarnService(String?token,?String?secret)?{
????????this.token?=?token;
????????this.secret?=?secret;
????}
????public?void?sendRobotMessage(DingTalkSendRequest?dingTalkSendRequest)?throws?Exception?{
????????String?json?=?JSONUtil.toJsonStr(dingTalkSendRequest);
????????String?sign?=?getSign();
????????String?body?=?HttpRequest.post(sign).contentType(ContentType.JSON.getValue()).body(json).execute().body();
????????log.info("釘釘機(jī)器人通知結(jié)果:{}",?body);
????}
????/**
?????*?獲取簽名
?????*
?????*?@return?返回簽名
?????*/
????private?String?getSign()?throws?Exception?{
????????long?timestamp?=?System.currentTimeMillis();
????????String?stringToSign?=?timestamp?+?"\n"?+?secret;
????????Mac?mac?=?Mac.getInstance("HmacSHA256");
????????mac.init(new?SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8),?"HmacSHA256"));
????????byte[]?signData?=?mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
????????return?ROBOT_SEND_URL?+?token?+?"×tamp="?+?timestamp?+?"&sign="?+?URLEncoder.encode(new?String(Base64.getEncoder().encode(signData)),?StandardCharsets.UTF_8.toString());
????}
????@Override
????protected?void?doSendText(String?message)?throws?Exception?{
????????DingTalkSendRequest?param?=?new?DingTalkSendRequest();
????????param.setMsgtype(DingTalkSendMsgTypeEnum.TEXT.getType());
????????param.setText(new?DingTalkSendRequest.Text(message));
????????sendRobotMessage(param);
????}
????@Override
????protected?void?doSendMarkdown(String?title,?String?message)?throws?Exception?{
????????DingTalkSendRequest?param?=?new?DingTalkSendRequest();
????????param.setMsgtype(DingTalkSendMsgTypeEnum.MARKDOWN.getType());
????????DingTalkSendRequest.Markdown?markdown?=?new?DingTalkSendRequest.Markdown(title,?message);
????????param.setMarkdown(markdown);
????????sendRobotMessage(param);
????}
}
5.5、WorkWeXinWarnService
主要實(shí)現(xiàn)了發(fā)送企業(yè)微信告警信息的邏輯
@Slf4j
public?class?WorkWeXinWarnService?extends?BaseWarnService?{
????private?static?final?String?SEND_MESSAGE_URL?=?"https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=%s";
????private?final?String?key;
????private?final?String?toUser;
????public?WorkWeXinWarnService(String?key,?String?toUser)?{
????????this.key?=?key;
????????this.toUser?=?toUser;
????}
????private?String?createPostData(WorkWeXinSendMsgTypeEnum?messageTye,?String?contentValue)?{
????????WorkWeXinSendRequest?wcd?=?new?WorkWeXinSendRequest();
????????wcd.setMsgtype(messageTye.getType());
????????List<String>?toUsers?=?Arrays.asList("@all");
????????if?(StringUtils.isNotEmpty(toUser))?{
????????????String[]?split?=?toUser.split("\\|");
????????????toUsers?=?Arrays.asList(split);
????????}
????????if?(messageTye.equals(WorkWeXinSendMsgTypeEnum.TEXT))?{
????????????WorkWeXinSendRequest.Text?text?=?new?WorkWeXinSendRequest.Text(contentValue,?toUsers);
????????????wcd.setText(text);
????????}?else?if?(messageTye.equals(WorkWeXinSendMsgTypeEnum.MARKDOWN))?{
????????????WorkWeXinSendRequest.Markdown?markdown?=?new?WorkWeXinSendRequest.Markdown(contentValue,?toUsers);
????????????wcd.setMarkdown(markdown);
????????}
????????return?JSONUtil.toJsonStr(wcd);
????}
????@Override
????protected?void?doSendText(String?message)?{
????????String?data?=?createPostData(WorkWeXinSendMsgTypeEnum.TEXT,?message);
????????String?url?=?String.format(SEND_MESSAGE_URL,?key);
????????String?resp?=?HttpRequest.post(url).body(data).execute().body();
????????log.info("send?work?weixin?message?call?[{}],?param:{},?resp:{}",?url,?data,?resp);
????}
????@Override
????protected?void?doSendMarkdown(String?title,?String?message)?{
????????String?data?=?createPostData(WorkWeXinSendMsgTypeEnum.MARKDOWN,?message);
????????String?url?=?String.format(SEND_MESSAGE_URL,?key);
????????String?resp?=?HttpRequest.post(url).body(data).execute().body();
????????log.info("send?work?weixin?message?call?[{}],?param:{},?resp:{}",?url,?data,?resp);
????}
}
5.6、MailWarnService
主要實(shí)現(xiàn)郵件告警邏輯
@Slf4j
public?class?MailWarnService?extends?BaseWarnService?{
????private?final?String?smtpHost;
????private?final?String?smtpPort;
????private?final?String?to;
????private?final?String?from;
????private?final?String?username;
????private?final?String?password;
????private?Boolean?ssl?=?true;
????private?Boolean?debug?=?false;
????public?MailWarnService(String?smtpHost,?String?smtpPort,?String?to,?String?from,?String?username,?String?password)?{
????????this.smtpHost?=?smtpHost;
????????this.smtpPort?=?smtpPort;
????????this.to?=?to;
????????this.from?=?from;
????????this.username?=?username;
????????this.password?=?password;
????}
????public?void?setSsl(Boolean?ssl)?{
????????this.ssl?=?ssl;
????}
????public?void?setDebug(Boolean?debug)?{
????????this.debug?=?debug;
????}
????@Override
????protected?void?doSendText(String?message)?throws?Exception?{
????????Properties?props?=?new?Properties();
????????props.setProperty("mail.smtp.auth",?"true");
????????props.setProperty("mail.transport.protocol",?"smtp");
????????props.setProperty("mail.smtp.host",?smtpHost);
????????props.setProperty("mail.smtp.port",?smtpPort);
????????props.put("mail.smtp.ssl.enable",?true);
????????Session?session?=?Session.getInstance(props);
????????session.setDebug(false);
????????MimeMessage?msg?=?new?MimeMessage(session);
????????msg.setFrom(new?InternetAddress(from));
????????for?(String?toUser?:?to.split(","))?{
????????????msg.setRecipient(MimeMessage.RecipientType.TO,?new?InternetAddress(toUser));
????????}
????????Map<String,?String>?map?=?JSONUtil.toBean(message,?Map.class);
????????msg.setSubject(map.get("subject"),?"UTF-8");
????????msg.setContent(map.get("content"),?"text/html;charset=UTF-8");
????????msg.setSentDate(new?Date());
????????Transport?transport?=?session.getTransport();
????????transport.connect(username,?password);
????????transport.sendMessage(msg,?msg.getAllRecipients());
????????transport.close();
????}
????@Override
????protected?void?doSendMarkdown(String?title,?String?message)?throws?Exception?{
????????log.warn("暫不支持發(fā)送Markdown郵件");
????}
}
6、AlarmAutoConfiguration自動(dòng)裝配類
運(yùn)用了springboot自定義的starter,再META-INF包下的配置文件spring.factories下,配置上該類
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ ????com.seven.buttemsg.autoconfigure.AlarmAutoConfiguration
自動(dòng)裝配類,用于裝載自定義的bean
@Slf4j
@Configuration
public?class?AlarmAutoConfiguration?{
????//?郵件相關(guān)配置裝載
????@Configuration
????@ConditionalOnProperty(prefix?=?MailConfig.PREFIX,?name?=?"enabled",?havingValue?=?"true")
????@EnableConfigurationProperties(MailConfig.class)
????static?class?MailWarnServiceMethod?{
????????@Bean
????????@ConditionalOnMissingBean(MailWarnService.class)
????????public?MailWarnService?mailWarnService(final?MailConfig?mailConfig)?{
????????????MailWarnService?mailWarnService?=?new?MailWarnService(mailConfig.getSmtpHost(),?mailConfig.getSmtpPort(),?mailConfig.getTo(),?mailConfig.getFrom(),?mailConfig.getUsername(),?mailConfig.getPassword());
????????????mailWarnService.setSsl(mailConfig.getSsl());
????????????mailWarnService.setDebug(mailConfig.getDebug());
????????????AlarmFactoryExecute.addAlarmLogWarnService(mailWarnService);
????????????return?mailWarnService;
????????}
????}
????//?企業(yè)微信相關(guān)配置裝載
????@Configuration
????@ConditionalOnProperty(prefix?=?WorkWeXinConfig.PREFIX,?name?=?"enabled",?havingValue?=?"true")
????@EnableConfigurationProperties(WorkWeXinConfig.class)
????static?class?WorkWechatWarnServiceMethod?{
????????@Bean
????????@ConditionalOnMissingBean(MailWarnService.class)
????????public?WorkWeXinWarnService?workWechatWarnService(final?WorkWeXinConfig?workWeXinConfig)?{
????????????return?new?WorkWeXinWarnService(workWeXinConfig.getKey(),?workWeXinConfig.getToUser());
????????}
????????@Autowired
????????void?setDataChangedListener(WorkWeXinWarnService?workWeXinWarnService)?{
????????????AlarmFactoryExecute.addAlarmLogWarnService(workWeXinWarnService);
????????}
????}
????//?釘釘相關(guān)配置裝載
????@Configuration
????@ConditionalOnProperty(prefix?=?DingTalkConfig.PREFIX,?name?=?"enabled",?havingValue?=?"true")
????@EnableConfigurationProperties(DingTalkConfig.class)
????static?class?DingTalkWarnServiceMethod?{
????????@Bean
????????@ConditionalOnMissingBean(DingTalkWarnService.class)
????????public?DingTalkWarnService?dingTalkWarnService(final?DingTalkConfig?dingtalkConfig)?{
????????????DingTalkWarnService?dingTalkWarnService?=?new?DingTalkWarnService(dingtalkConfig.getToken(),?dingtalkConfig.getSecret());
????????????AlarmFactoryExecute.addAlarmLogWarnService(dingTalkWarnService);
????????????return?dingTalkWarnService;
????????}
????}
????//?消息模板配置裝載
????@Configuration
????@ConditionalOnProperty(prefix?=?TemplateConfig.PREFIX,?name?=?"enabled",?havingValue?=?"true")
????@EnableConfigurationProperties(TemplateConfig.class)
????static?class?TemplateConfigServiceMethod?{
????????@Bean
????????@ConditionalOnMissingBean
????????public?AlarmTemplateProvider?alarmTemplateProvider(TemplateConfig?templateConfig)?{
????????????if?(TemplateSource.FILE?==?templateConfig.getSource())?{
????????????????return?new?YamlAlarmTemplateProvider(templateConfig);
????????????}?else?if?(TemplateSource.JDBC?==?templateConfig.getSource())?{
????????????????//?數(shù)據(jù)庫(如mysql)讀取文件,未實(shí)現(xiàn),可自行擴(kuò)展
????????????????return?new?JdbcAlarmTemplateProvider(templateId?->?null);
????????????}?else?if?(TemplateSource.MEMORY?==?templateConfig.getSource())?{
????????????????//?內(nèi)存(如redis,本地內(nèi)存)讀取文件,未實(shí)現(xiàn),可自行擴(kuò)展
????????????????return?new?MemoryAlarmTemplateProvider(templateId?->?null);
????????????}
????????????return?new?YamlAlarmTemplateProvider(templateConfig);
????????}
????}
????@Bean
????public?AlarmAspect?alarmAspect(@Autowired(required?=?false)?AlarmTemplateProvider?alarmTemplateProvider)?{
????????return?new?AlarmAspect(alarmTemplateProvider);
????}
}
四、總結(jié)
主要借助spring的切面技術(shù),以及springboot的自動(dòng)裝配原理,實(shí)現(xiàn)了發(fā)送告警邏輯。對(duì)業(yè)務(wù)代碼無侵入,只需要在業(yè)務(wù)代碼上標(biāo)記注解,就可實(shí)現(xiàn)可插拔的功能,比較輕量。
以上就是利用AOP實(shí)現(xiàn)系統(tǒng)告警的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于AOP系統(tǒng)告警的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用spring的restTemplate注意點(diǎn)
這篇文章主要介紹了使用spring的restTemplate注意點(diǎn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
SpringCloud OpenFeign基本介紹與實(shí)現(xiàn)示例
OpenFeign源于Netflix的Feign,是http通信的客戶端。屏蔽了網(wǎng)絡(luò)通信的細(xì)節(jié),直接面向接口的方式開發(fā),讓開發(fā)者感知不到網(wǎng)絡(luò)通信細(xì)節(jié)。所有遠(yuǎn)程調(diào)用,都像調(diào)用本地方法一樣完成2023-02-02
Springboot基于BCrypt非對(duì)稱加密字符串的實(shí)現(xiàn)
本文主要介紹了Springboot基于BCrypt非對(duì)稱加密字符串的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
淺析Java ClassName.this中類名.this關(guān)鍵字的理解
Java ClassName.this中類名.this關(guān)鍵字 的理解大家都了解多少,有不太了解的朋友可以參考下本文一起學(xué)習(xí)學(xué)習(xí)2016-05-05
Java中的權(quán)重算法(如Dubbo的負(fù)載均衡權(quán)重)詳解
這篇文章主要介紹了Java中的權(quán)重算法(如Dubbo的負(fù)載均衡權(quán)重)詳解,負(fù)載均衡,其含義就是指將負(fù)載進(jìn)行平衡、分?jǐn)偟蕉鄠€(gè)操作單元上進(jìn)行運(yùn)行,例如FTP服務(wù)器、Web服務(wù)器、企業(yè)核心應(yīng)用服務(wù)器和其它主要任務(wù)服務(wù)器等,從而協(xié)同完成工作任務(wù),需要的朋友可以參考下2023-08-08
使用list stream:對(duì)List中的對(duì)象先進(jìn)行排序再獲取前n個(gè)對(duì)象
這篇文章主要介紹了使用list stream:對(duì)List中的對(duì)象先進(jìn)行排序再獲取前n個(gè)對(duì)象,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
解決springboot項(xiàng)目上傳文件出現(xiàn)臨時(shí)文件目錄為空的問題
這篇文章主要介紹了解決springboot項(xiàng)目上傳文件出現(xiàn)臨時(shí)文件目錄為空的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09
如何解決SpringBoot定時(shí)任務(wù)報(bào)錯(cuò)Unexpected error occurred 
這篇文章主要介紹了如何解決SpringBoot定時(shí)任務(wù)報(bào)錯(cuò)Unexpected error occurred in scheduled task問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
SpringBoot入門編寫第一個(gè)程序Helloworld
這篇文章是Springboot入門篇,來教大家編寫第一個(gè)Springboot程序Helloworld,文中附有詳細(xì)的示例代碼,有需要的同學(xué)可以借鑒參考下2021-09-09

