背景
利用钉钉的自定义机器人来发送项目中的异常信息到钉钉业务群
目标
报警效果图
@指定处理人的效果图
开始
首先请阅读钉钉开发文档,任何官方的文档都是最好的文章资料
整个接入钉钉机器人只有两步
获取自定义机器人的Webhook
-
点击你
需要发送告警的群
->群设置
->智能群助手
->添加机器人
-
设置自定义机器人的信息
机器人名字
根据你的业务随便给个命名
添加到群组
接收告警信息的群
安全设置
- 自定义关键词:最多可以设置10个关键词,消息中至少包含其中1个关键词才可以发送成功
- 加签:
- IP地址:设定后,只有来自IP地址范围内的请求才会被正常处理。支持两种设置方式:IP地址和IP地址段,暂不支持IPv6地址白名单,格式如下。
-
得到
Webhook
编写代码发送告警信息
上面其实说了很多废话,看一眼钉钉的官方文档就行了,下面开始介绍基于JAVA编写的告警代码
-
发送告警
/** * 发送报警 * * @param msgType msg类型 * @param throwable 异常 * @param isAt 是否需要@人 * @param isAtAll 是否@所有人 * @param atUsers @用户列表 传手机号 */ public static void alarm(MsgType msgType, Throwable throwable, boolean isAt, boolean isAtAll, List<String> atUsers) { DingConfig config = dingRobotUtil.dingConfig; if (!config.isEnable()) { return; } log.info("钉钉机器人 - 发送报警信息"); DingTalkClient client = new DefaultDingTalkClient(config.getWebhook()); OapiRobotSendRequest request = new OapiRobotSendRequest(); request.setMsgtype(msgType.getValue()); OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text(); // 内容必须包含 "报错" 等关键字,否则发送会失败,关键字可以在钉钉机器人管理修改 text.setContent(buildErrMsg(throwable)); request.setText(text); if (isAt) { OapiRobotSendRequest.At at = new OapiRobotSendRequest.At(); if (CollectionUtils.isEmpty(atUsers)) { at.setAtMobiles(atUsers); } else { at.setAtMobiles(config.getAtMobiles()); } // 是否@所有人 at.setIsAtAll(isAtAll); // 开发者文档显示可以这样传参,其实没有这个参数 setAtMobiles 用来指定要@的用户 // at.setAtUserIds(Arrays.asList("109929","32099")); request.setAt(at); } try { log.info("钉钉机器人 - 报警内容:{}", JSON.toJSON(request)); OapiRobotSendResponse response = client.execute(request); log.info("钉钉机器人 - 报警结束:{}", JSON.toJSON(response)); } catch (Exception e) { log.error("钉钉机器人 - 报警失败:{}", e); } }
-
构建消息内容
- 下面是我个人总结的一些希望直接在报警信息里的内容,比如 时间,报警服务器,log的路径,异常信息等等,可以根据项目情况随意添加自己想看的信息
@SneakyThrows private static String buildErrMsg(Throwable throwable) { StringBuffer msg = new StringBuffer(); InetAddress inetAddress = InetAddress.getLocalHost(); msg.append("报错日期: ").append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); // 注意 主机名和IP,需要在服务器的hosts文件里修改,否则拿到的就是默认的localhost和127.0.0.1 msg.append("\n主机名称: ").append(inetAddress.getHostName()); msg.append("\n主机IP: ").append(inetAddress.getHostAddress()).append("\n"); msg.append("\n日志路径: ").append(getLogPath()).append("\n"); msg.append("\n异常方法: ").append(throwable.getStackTrace()[0]).append("\n"); // 业务自定义的异常 if (throwable instanceof ServiceException) { ServiceException e = (ServiceException) throwable; msg.append("\n异常信息: ").append(e.getMsg()).append("\n"); } // 代码运行未知异常 if (throwable instanceof Exception) msg.append("\n异常信息: ").append(throwable).append("\n"); return msg.toString(); } /** * 获取日志路径 * * @return {@link String} * @throws IOException ioexception */ public static String getLogPath() throws IOException { LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); ch.qos.logback.classic.Logger logger = context.getLogger("ROOT"); //输出日志文件的appender的name属性 FileAppender fileAppender = (FileAppender) logger.getAppender("infoLog"); File file = new File(fileAppender.getFile()); return file.getCanonicalPath(); }
-
触发点
我的项目里用的
@RestControllerAdvice
来捕获全局异常,所以直接在我需要告警的异常处理方法里直接调用alarm()
方法就行了,你可以根据你的业务需求来自动触发或手动触发@ExceptionHandler(ServiceException.class) public ResultBody resolveBusinessException(ServiceException e) { log.info("系统内部错误:", e); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); ResultBody result = ResultBody.fail(e.getCode(), e.getMsg()); request.setAttribute(RESPONSE_DATA, result); DingRobotUtil.alarm(DingRobotUtil.MsgType.TEXT, e,true, false, null); return result; }
于2021-6-8 周二 深圳