利用AOP實(shí)現(xiàn)系統(tǒng)告警的方法詳解
一、業(yè)務(wù)背景
在開(kāi)發(fā)的過(guò)程中會(huì)遇到各種各樣的開(kāi)發(fā)問(wèn)題,服務(wù)器宕機(jī)、網(wǎng)絡(luò)抖動(dòng)、代碼本身的bug等等。針對(duì)代碼的bug,我們可以提前預(yù)支,通過(guò)發(fā)送告警信息來(lái)警示我們?nèi)ジ深A(yù),盡早處理。
二、告警的方式
1、釘釘告警
通過(guò)在企業(yè)釘釘群,添加群機(jī)器人的方式,通過(guò)機(jī)器人向群內(nèi)發(fā)送報(bào)警信息。至于釘釘機(jī)器人怎么創(chuàng)建,發(fā)送消息的api等等,請(qǐng)參考官方文檔
2、企業(yè)微信告警
同樣的套路,企業(yè)微信也是,在企業(yè)微信群中,添加群機(jī)器人。通過(guò)機(jī)器人發(fā)送告警信息。具體請(qǐng)看官方文檔
3、郵件告警
與上述不同的是,郵件是發(fā)送給個(gè)人的,當(dāng)然也可以是批量發(fā)送,只實(shí)現(xiàn)了發(fā)送文本格式的方式,至于markdown格式,有待考察。郵件發(fā)送相對(duì)比較簡(jiǎn)單,這里就不展開(kāi)贅述。
三、源碼解析
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: ?????#?開(kāi)啟釘釘發(fā)送告警 ??????enabled:?true ?????#?釘釘群機(jī)器人唯一的token ??????token:?xxxxxx ?????#?安全設(shè)置:加簽的密鑰 ??????secret:?xxxxxxx
2.2、企業(yè)微信配置文件
spring: ??alarm: ????wechat: ?????#?開(kāi)啟企業(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: ??????#?開(kāi)啟通過(guò)模板配置 ??????enabled:?true ??????#?配置模板來(lái)源為文件 ??????source:?FILE ??????#?配置模板數(shù)據(jù) ??????templates: ????????errorTemp: ??????????templateId:?errorTemp ??????????templateName:?服務(wù)異常模板 ??????????templateContent:?這里是配置模板的內(nèi)容
spring:alarm:template:enabled
,Boolean類型,表示開(kāi)啟告警消息使用模板發(fā)送。spring:alarm:template:source
,模板來(lái)源,枚舉類:JDBC(數(shù)據(jù)庫(kù))、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ù)庫(kù)(如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ù)代碼無(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-10SpringCloud OpenFeign基本介紹與實(shí)現(xiàn)示例
OpenFeign源于Netflix的Feign,是http通信的客戶端。屏蔽了網(wǎng)絡(luò)通信的細(xì)節(jié),直接面向接口的方式開(kāi)發(fā),讓開(kāi)發(fā)者感知不到網(wǎng)絡(luò)通信細(xì)節(jié)。所有遠(yuǎn)程調(diào)用,都像調(diào)用本地方法一樣完成2023-02-02Springboot基于BCrypt非對(duì)稱加密字符串的實(shí)現(xiàn)
本文主要介紹了Springboot基于BCrypt非對(duì)稱加密字符串的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(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-05Java中的權(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í)文件目錄為空的問(wèn)題
這篇文章主要介紹了解決springboot項(xiàng)目上傳文件出現(xiàn)臨時(shí)文件目錄為空的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09如何解決SpringBoot定時(shí)任務(wù)報(bào)錯(cuò)Unexpected error occurred 
這篇文章主要介紹了如何解決SpringBoot定時(shí)任務(wù)報(bào)錯(cuò)Unexpected error occurred in scheduled task問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08SpringBoot入門(mén)編寫(xiě)第一個(gè)程序Helloworld
這篇文章是Springboot入門(mén)篇,來(lái)教大家編寫(xiě)第一個(gè)Springboot程序Helloworld,文中附有詳細(xì)的示例代碼,有需要的同學(xué)可以借鑒參考下2021-09-09