
在现代微服务架构中,及时监控和响应系统异常对于保障系统的稳定性和可靠性至关重要。钉钉作为企业级通讯工具,提供了强大的机器人功能,可以方便地将系统异常信息实时推送到指定的群聊中。本文将介绍如何在Spring Boot项目中整合钉钉机器人,实现异常日志的自动通知功能。本文将利用腾讯云 AI 代码助手完成本次教程,腾讯云 AI 代码助手可以帮助开发者快速生成代码、优化现有代码,并提供技术指导。
首先,在项目的 pom.xml 文件中引入钉钉SDK依赖,以便与钉钉API进行交互。
<!-- 引入钉钉SDK -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibaba-dingtalk-service-sdk</artifactId>
<version>2.0.0</version>
</dependency>注意:根据实际需要,可能需要调整版本号或使用其他钉钉SDK依赖。
为了捕获应用中的所有未处理异常,并将其发送到钉钉群聊中,我们需要定义一个全局异常处理器,这里就可以利用腾讯云 AI 代码助手,帮助生成。
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.ServletRequest;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Objects;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 全局异常处理
* @param e 异常对象
* @param request HTTP请求
* @return 错误响应
*/
@ExceptionHandler(value = Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Result<?> exceptionHandler(Exception e, ServletRequest request){
// 获取请求参数
if (Objects.nonNull(request) && request instanceof ContentCachingRequestWrapper) {
ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
try {
String requestBody = StringUtils.toEncodedString(
wrapper.getContentAsByteArray(),
Charset.forName(wrapper.getCharacterEncoding())
);
log.info("未知异常 request ip: {}, url: {}, body: {}",
wrapper.getRemoteAddr(),
wrapper.getRequestURI(),
requestBody);
} catch (Exception ex) {
log.warn("解析请求体失败", ex);
}
}
log.error("未知异常", e);
// 通知钉钉
DingTalkSendMsgUtil.sendToDingTalk(e);
return Result.fail("系统异常,请联系管理员");
}
@ExceptionHandler(value = MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Result<?> exceptionHandler(MethodArgumentNotValidException e) {
log.debug("传入参数异常", e);
String errorMessage = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
return Result.fail(errorMessage != null ? errorMessage : e.getMessage());
}
}
/**
* 请求包装过滤器,用于缓存请求体以便多次读取
*/
@Component
public class RequestWrapperFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(new ContentCachingRequestWrapper(httpServletRequest), httpServletResponse);
}
}
首先,打开钉钉应用,可以找个测试群聊创建机器人。

配置完成后,系统会生成一个Webhook URL,这个URL就是后续钉钉群推送消息的接口地址,拷贝webhook。

接下来,需要定义一个工具类,用于将异常信息发送到钉钉群聊中,这里也可以使用腾讯云 AI 代码助手进行生成,最终代码如下:
import com.aliyun.dingtalk.api.DefaultDingTalkClient;
import com.aliyun.dingtalk.api.DingTalkClient;
import com.aliyun.dingtalk.api.request.OapiRobotSendRequest;
import com.aliyun.dingtalk.api.response.OapiRobotSendResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.output.StringWriter;
import org.apache.commons.io.output.PrintWriter;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class DingTalkSendMsgUtil {
// 配置自己创建的钉钉群webhook - 实际使用时替换为真实token
private static String WEBHOOK_URL = "https://oapi.dingtalk.com/robot/send?access_token=YOUR_ACCESS_TOKEN";
// 可选:设置签名密钥(如果启用了安全设置)
// private static final String SECRET = "your_secret";
/**
* 发送异常信息到钉钉群聊
* @param exception 异常对象
*/
public static void sendToDingTalk(Exception exception) {
try {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
exception.printStackTrace(pw);
DingTalkClient client = new DefaultDingTalkClient(WEBHOOK_URL);
OapiRobotSendRequest req = new OapiRobotSendRequest();
// 构建消息内容
OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
String traceId = MDC.get("traceId") != null ? MDC.get("traceId") : "N/A";
text.setContent(String.format(
"[系统异常通知]\n" +
"TraceID: %s\n" +
"异常时间: %s\n" +
"异常信息:\n%s",
traceId,
java.time.LocalDateTime.now(),
sw.toString()
));
req.setMsgtype("text");
req.setText(text);
// 如果启用了签名安全设置,需要添加签名
// String timestamp = String.valueOf(System.currentTimeMillis());
// String sign = SignUtil.sign(SECRET, timestamp);
// req.setTimestamp(timestamp);
// req.setSign(sign);
OapiRobotSendResponse response = client.execute(req);
if (!response.isSuccess()) {
log.error("钉钉消息发送失败: {}", response.getErrmsg());
}
} catch (Exception e) {
log.error("消息推送钉钉发送异常", e);
}
}
/**
* 发送富文本消息到钉钉群聊
* @param title 消息标题
* @param content 消息内容
*/
public static void sendRichTextToDingTalk(String title, String content) {
try {
DingTalkClient client = new DefaultDingTalkClient(WEBHOOK_URL);
OapiRobotSendRequest req = new OapiRobotSendRequest();
req.setMsgtype("markdown");
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
markdown.setTitle(title);
markdown.setText(String.format(
"### %s\n\n%s\n\n> **TraceID**: %s\n> **时间**: %s",
title,
content,
MDC.get("traceId") != null ? MDC.get("traceId") : "N/A",
java.time.LocalDateTime.now()
));
req.setMarkdown(markdown);
OapiRobotSendResponse response = client.execute(req);
if (!response.isSuccess()) {
log.error("钉钉富文本消息发送失败: {}", response.getErrmsg());
}
} catch (Exception e) {
log.error("钉钉富文本消息发送异常", e);
}
}
}
注意事项:
为了验证异常通知功能是否正常工作,可以创建一个测试控制器,故意抛出一个异常。
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class DingdingExceptionController {
@RequestMapping(value = "/dingding/excep", method = RequestMethod.POST)
public void triggerException() {
// 故意引发 ArithmeticException
int i = 1 / 0;
}
@RequestMapping(value = "/dingding/test", method = RequestMethod.GET)
public void testDingTalk() {
try {
// 模拟业务异常
throw new RuntimeException("测试异常 - 这是业务逻辑错误");
} catch (Exception e) {
DingTalkSendMsgUtil.sendToDingTalk(e);
}
}
}
http://localhost:8080/dingding/excep发送POST请求\
• 或者直接访问http://localhost:8080/dingding/test触发测试异常6.1 自定义消息格式
可以根据需要自定义消息格式,例如添加更多上下文信息:
public static void sendDetailedErrorToDingTalk(Exception exception, String contextInfo) {
try {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
exception.printStackTrace(pw);
DingTalkClient client = new DefaultDingTalkClient(WEBHOOK_URL);
OapiRobotSendRequest req = new OapiRobotSendRequest();
req.setMsgtype("markdown");
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
markdown.setTitle("[严重错误] 系统异常通知");
markdown.setText(String.format(
"### [严重错误] 系统异常通知\n\n" +
"**TraceID**: %s\n\n" +
"#### 异常时间\n%s\n\n" +
"#### 异常信息\n```java\n%s\n```\n\n" +
"#### 上下文信息\n> %s\n\n" +
"#### 请求信息\n> 请检查相关日志获取详细请求信息",
MDC.get("traceId") != null ? MDC.get("traceId") : "N/A",
java.time.LocalDateTime.now(),
sw.toString(),
contextInfo != null ? contextInfo : "无"
));
req.setMarkdown(markdown);
client.execute(req);
} catch (Exception e) {
log.error("发送详细错误消息到钉钉失败", e);
}
}
6.2 异常分类处理
根据不同的异常类型发送不同级别的通知:
public static void handleException(Exception e) {
if (e instanceof BusinessException) {
// 业务异常,发送普通通知
sendToDingTalk(e);
} else if (e instanceof DatabaseException) {
// 数据库异常,发送警告通知
sendRichTextToDingTalk("数据库异常警告", e.getMessage());
} else {
// 其他严重异常,发送紧急通知
sendRichTextToDingTalk("⚠️ 系统紧急异常 ⚠️",
"系统发生严重错误,请立即处理!\n\n" + e.getMessage());
}
}
通过上述步骤,我们成功地在Spring Boot项目中整合了钉钉机器人,实现了系统异常日志的自动通知功能。这不仅提高了开发团队对系统异常的响应速度,还增强了系统的可维护性和稳定性。在实际应用中,可以根据需求进一步优化异常处理逻辑,例如根据不同的异常类型发送不同的通知,或者集成更多的消息推送渠道。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。