项目接入钉钉机器人实现告警

2,669 阅读3分钟

背景

利用钉钉的自定义机器人来发送项目中的异常信息到钉钉业务群

目标

报警效果图 image.png

@指定处理人的效果图 image.png

开始

首先请阅读钉钉开发文档,任何官方的文档都是最好的文章资料

整个接入钉钉机器人只有两步

获取自定义机器人的Webhook

  1. 点击你需要发送告警的群 -> 群设置-> 智能群助手 -> 添加机器人

    image.png

    image.png

  2. 设置自定义机器人的信息

    image.png

    机器人名字

    根据你的业务随便给个命名

    添加到群组

    接收告警信息的群

    安全设置

    • 自定义关键词:最多可以设置10个关键词,消息中至少包含其中1个关键词才可以发送成功
    • 加签:
    • IP地址:设定后,只有来自IP地址范围内的请求才会被正常处理。支持两种设置方式:IP地址和IP地址段,暂不支持IPv6地址白名单,格式如下。
  3. 得到Webhook

    image.png

编写代码发送告警信息

上面其实说了很多废话,看一眼钉钉的官方文档就行了,下面开始介绍基于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 周二 深圳