背景
利用 钉钉 的自定义机器人来发送项目中的异常信息到钉钉业务群
目标
报警效果图
@指定处理人的效果图
开始
首先请阅读 钉钉开发文档 ,任何官方的文档都是最好的文章资料
整个接入钉钉机器人只有两步
获取自定义机器人的Webhook
- 点击你 需要发送告警的群 -> 群设置 -> 智能群助手 -> 添加机器人
2、设置自定义机器人的信息
机器人名字
添加到群组
接收告警信息的群
安全设置
自定义关键词:最多可以设置10个关键词,消息中至少包含其中1个关键词才可以发送成功加签:IP地址:设定后,只有来自IP地址范围内的请求才会被正常处理。支持两种设置方式:IP地址和IP地址段,暂不支持IPv6地址白名单,格式如下。
编写代码发送告警信息
上面其实说了很多废话,看一眼钉钉的官方文档就行了,下面开始介绍基于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 IO Exception {
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
ch.qos.logback.classic.Logger logger = context.getLogger("ROOT");
//输出日志文件的appender的name属性
FileAppender fileAppender = ( File Appender) 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;
}
原文: