xxl-job默认的告警通知方式是发送邮件,工作中不易及时的收到通知并作出响应,所以想要调整下告警的通知方式,查阅文档资料,发现可以扩展xxl-job的源码实现支持。
主要思路就是根据xxl-job现有的告警机制,扩展实现企业微信机器人的告警功能,接下来就记录下xxl-job新增企业微信告警通知功能。
- 从官方网站上下载xxl-job源码,导入IDEA。
官方地址:https://github.com/xuxueli/xxl-job 或 https://gitee.com/xuxueli0323/xxl-job。 - xxl-job现有的告警功能。可以发现只要实现JobAlarm这个接口就可以了。
@Component
public class JobAlarmer implements ApplicationContextAware, InitializingBean {private static Logger logger = LoggerFactory.getLogger(JobAlarmer.class);private ApplicationContext applicationContext;private List<JobAlarm> jobAlarmList;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}@Overridepublic void afterPropertiesSet() throws Exception {Map<String, JobAlarm> serviceBeanMap = applicationContext.getBeansOfType(JobAlarm.class);if (serviceBeanMap != null && serviceBeanMap.size() > 0) {jobAlarmList = new ArrayList<JobAlarm>(serviceBeanMap.values());}}public boolean alarm(XxlJobInfo info, XxlJobLog jobLog) {boolean result = false;if (jobAlarmList!=null && jobAlarmList.size()>0) {result = true; for (JobAlarm alarm: jobAlarmList) {boolean resultItem = false;try {resultItem = alarm.doAlarm(info, jobLog);} catch (Exception e) {logger.error(e.getMessage(), e);}if (!resultItem) {result = false;}}}return result;}}
- 扩展实现企业微信机器人的告警功能。
在企业微信终端某个群组添加机器人之后,创建者可以在机器人详情页看到该机器人特有的webhookurl。开发者可以向这个地址发起HTTP POST请求,即可实现给该群组发送消息。相关资料可以参考:https://developer.work.weixin.qq.com/document/path/91770
后续可能还会有其他告警通知的需求(比如钉钉告警通知等),就先抽取一个类出来,方便后续扩展。
public abstract class AbstractJobAlarm implements JobAlarm {protected static final Logger logger = LoggerFactory.getLogger(AbstractJobAlarm.class);protected static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");protected RestTemplate restTemplate = new RestTemplate();public abstract String containsKeyword();public abstract String genJobAlarmRequestUrl(String keyword);public abstract Object genJobAlarmRequestBody(XxlJobInfo xxlJobInfo, XxlJobLog xxlJobLog);@Overridepublic boolean doAlarm(XxlJobInfo xxlJobInfo, XxlJobLog xxlJobLog) {if (null == xxlJobInfo) {return true;}String alarmEmail = xxlJobInfo.getAlarmEmail();if (null == alarmEmail || "".equals(alarmEmail) || !alarmEmail.contains(containsKeyword())) {return true;}boolean alarmResult = true;Object jobAlarmRequestBody = genJobAlarmRequestBody(xxlJobInfo, xxlJobLog);String[] keywordArray = alarmEmail.split(",");for (int i = 0, len = keywordArray.length; i < len; i++) {try {String jobAlarmRequestUrl = genJobAlarmRequestUrl(keywordArray[i]);logger.info("job alarm request url {} body {}", jobAlarmRequestUrl, jobAlarmRequestBody);restTemplate.postForEntity(jobAlarmRequestUrl, jobAlarmRequestBody, Object.class);} catch (Exception e) {logger.error(">>>>>>>>>>> xxl-job, job fail alarm send error, job log id:{}", xxlJobLog.getId(), e);alarmResult = false;}}return alarmResult;}protected String buildJobAlarmContentV1(XxlJobInfo xxlJobInfo, XxlJobLog xxlJobLog) {StringBuilder content = new StringBuilder();content.append("【告警信息】\n");content.append("负责人: ").append(buildAuthor(xxlJobInfo.getAuthor())).append("\n");content.append("描述: ").append(xxlJobInfo.getJobDesc()).append("\n");content.append("JOB_ID: ").append(xxlJobLog.getJobId()).append("\n");content.append("JOB_GROUP: ").append(xxlJobLog.getJobGroup()).append("\n");content.append("执行器名称: ").append(xxlJobInfo.getExecutorHandler()).append("\n");content.append("执行器IP: ").append(xxlJobLog.getExecutorAddress()).append("\n");content.append("任务参数: ").append(xxlJobLog.getExecutorParam()).append("\n");content.append("告警时间: ").append(SDF.format(new Date())).append("\n");content.append("LOG_ID: ").append(xxlJobLog.getId()).append("\n");Date triggerTime = xxlJobLog.getTriggerTime();if (null != triggerTime) {content.append("TRIGGER_TIME: ").append(SDF.format(triggerTime)).append("\n");}String triggerMsg = xxlJobLog.getTriggerMsg();if (null != triggerMsg && !"".equals(triggerMsg.trim())) {content.append("TRIGGER_MSG: ").append(triggerMsg.replace("<br>", "\n")).append("\n");}Date handleTime = xxlJobLog.getHandleTime();if (null != handleTime) {content.append("HANDLE_TIME: ").append(SDF.format(handleTime)).append("\n");}String handleMsg = xxlJobLog.getHandleMsg();if (null != handleMsg && !"".equals(handleMsg.trim())) {content.append("HANDLE_MSG: ").append(handleMsg).append("\n");}return content.toString();}private String buildAuthor(String author) {return null == author || "".equals(author) ? author : String.join(",",Arrays.stream(author.split(",")).map(v -> "@" + v).collect(Collectors.toList()));}}
public class QyWeiXinJobAlarm extends AbstractJobAlarm {@Value("${qyweixin.robot.webhook.url.prefix:https://qyapi.weixin.qq.com/cgi-bin/webhook/send?}")private String webhookUrlPrefix;@Overridepublic String containsKeyword() {return "key";}@Overridepublic String genJobAlarmRequestUrl(String keyword) {return webhookUrlPrefix + keyword;}@Overridepublic Object genJobAlarmRequestBody(XxlJobInfo xxlJobInfo, XxlJobLog xxlJobLog) {HashMap<String, Object> requestBodyMap = new HashMap<>();try {ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(xxlJobLog.getExecutorAddress());if (null != executorBiz) {ReturnT<LogResult> resultReturnT = executorBiz.log(new LogParam(xxlJobLog.getTriggerTime().getTime(), xxlJobLog.getId(), 1));if (null != resultReturnT) {LogResult logResult = resultReturnT.getContent();if (null != logResult && null != logResult.getLogContent()) {logger.info("return result log {}", logResult.getLogContent());}}}} catch (Exception e) {logger.error(e.getMessage(), e);}requestBodyMap.put("msgtype", "text");HashMap<String, Object> textMap = new HashMap<>();textMap.put("content", buildJobAlarmContentV1(xxlJobInfo, xxlJobLog));String author = xxlJobInfo.getAuthor();if (null != author && !"".equals(author)) {List<String> authors = Arrays.stream(author.split(",")).collect(Collectors.toList());textMap.put("mentioned_list", authors);textMap.put("mentioned_mobile_list", authors);}requestBodyMap.put("text", textMap);return requestBodyMap;}}
- 自此,企业微信告警通知扩展实现完成,重新打包部署即可。没有调整修改前端的界面,所以只需要在原先的邮件文本框输入key=xxxxxx,key=xxxxxx即可。