httpServletRequest.getParameterMap()为空_凡凡轶崔的博客-CSDN博客
群机器人配置说明 - 接口文档 - 企业微信开发者中心 (qq.com)
由于项目中暂时没有接入日志系统,导致平时查看异常信息非常的不方便。而且每次都需要产品,测试发现了才知道出错了,这影响不太好。
于是结合 切面 + 全局异常拦截 等技术实现结合企微的实时告警。
我是利用企微中提供的群机器人发布异常信息,新建一个机器人会提供一个Webhook地址
开发者可以按提供的Webhook发起HTTP POST 请求,即可实现给该群组发送消息。具体可以参考文章:群机器人配置说明 - 接口文档 - 企业微信开发者中心 (qq.com)
使用的消息内容类型:markdown类型
{
"msgtype": "markdown",
"markdown": {
"content": "实时新增用户反馈<font color=\"warning\">132例</font>,请相关同事注意。\n
>类型:<font color=\"comment\">用户反馈</font>
>普通用户反馈:<font color=\"comment\">117例</font>
>VIP用户反馈:<font color=\"comment\">15例"
}
}
参数 | 是否必填 | 说明 |
---|---|---|
msgtype | 是 | 消息类型,此时固定为markdown |
content | 是 | markdown内容,最长不超过4096个字节,必须是utf8编码 |
使用原因:参数较少并且内容长度支持较长。
异常告警机器人代码:
@Component
@Slf4j
public class ExceptionMsgBot {
private static final String METHOD_GET = "GET";
private static final String METHOD_POST = "POST";
/**
* 企微机器人webhook地址
*/
@Value("${ExceptionMsgBot.url}")
private String url;
/**
* 是否启用
*/
@Value("${ExceptionMsgBot.isEnable:true}")
private Boolean isEnable;
/**
* 最大长度
*/
@Value("${ExceptionMsgBot.maxSize:2500}")
private int maxSize;
public void send(Exception e, HttpServletRequest request) {
if (!isEnable) {
return;
}
String title = "标题";
//时间
String now = DateUtil.format(new Date(), DatePattern.NORM_DATETIME_FORMAT);
//请求url
String api = request.getRequestURL().toString();
//请求参数
String params = JSONUtil.toJsonStr(request.getAttribute("params"));
if (!StringUtils.isEmpty(params)) {
//将json转成普通string
params = params.replace("\"","\\\"");
}
//服务器IP
String serverIP = NetUtil.getLocalhostStr();
//客户端IP
String clientIP = request.getRemoteAddr();
String textMsg = "{\n" +
" \"msgtype\": \"markdown\",\n" +
" \"markdown\": {\n" +
" \"content\": \"" + title + "\\n\n" +
" 【url】: " + api + " \n" +
" 【参数】: " + params + " \n" +
" 【时间】: " + now + " \n" +
" 【serverIp】: " + serverIP + " \n" +
" 【clientIp】: " + clientIP + " \n" +
" 【message】: " + e.getMessage() + " \n" +
" 【cause】: " + e.getCause() + " \n" +
" 【StackTrace】: " + e + " \"\n" +
" }\n" +
"}\n";
//发送消息
HttpResponse response = HttpRequest
.post(url)
.header(Header.CONTENT_TYPE, "application/json; charset=UTF-8")
.body(textMsg)
.execute();
log.info("【异常消息发送结果】 {}", response.body());
}
}
这次设计的异常信息主要展示8点
除了请求参数需要特殊处理,其他参数都可以方便获取。
因为每次请求接口,项目中会有个全局日志切面类打印输出每次请求的信息。
这个全局日志切面类会在进入controller之前进行拦截,从而可以获取到每次请求的参数。然后将参数封装到request,传递给内部web容器使用,也就是本文章的异常告警机器人使用。
@Aspect
@Configuration
@Slf4j
public class GlobalControllerLogAspect {
@Pointcut("execution (* com.*..controller..*.*(..))")
public void controllers() {
}
@Around("controllers()")
public Object aroundProcess(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
long startTime = System.currentTimeMillis();
Object proceed = null;
try {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String uid = request.getHeader(HeaderConstant.UID);
List<Object> params = new ArrayList<>();
if (args != null && args.length > 0) {
for (Object arg : args) {
if (arg instanceof HttpServletRequest || arg instanceof HttpServletResponse) {
continue;
} else if (arg instanceof MultipartFile) {
continue;
} else {
params.add(arg);
}
}
}
//保存请求参数
request.setAttribute("params", JSONUtil.toJsonStr(params));
log.info("接口方法:{},uid:{},请求参数:{}", request.getRequestURL().toString(), uid, JSONUtil.toJsonStr(params));
proceed = joinPoint.proceed(args);
log.info("响应结果:{},耗时:{}ms", JSONUtil.toJsonStr(proceed), System.currentTimeMillis() - startTime);
} finally {
}
return proceed;
}
}
可以在 joinPoint(连接点) 获取到请求参数。
异常告警机器人在全局异常拦截器埋点
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@Autowired
private ExceptionMsgBot bot;
@ExceptionHandler(value = Exception.class)
public void handle(Exception e, HttpServletRequest request, HttpServletResponse response) {
log.error("接口[{}]异常, ", request.getRequestURI(), e);
CommonResult<Object> result = CommonResult.failed("系统出了点小问题");
//埋点
bot.send(e, request);
String json = JSONUtil.toJsonStr(result);
response.setHeader("Content-Type", "application/json; charset=UTF-8");
try {
response.getWriter().write(json);
} catch (IOException ex) {
log.error("接口[{}]IO异常, ", request.getRequestURI(), ex);
}
}
}
当有异常信息被全局异常拦截器捕获时,保存异常日志的同时,发送告警信息通知开发等相关人员处理。
效果:
异常告警,请相关同事注意!
【url】: http://172.18.139.x:9001/xxx
【参数】: ["uid",{"f1":9,"f2":2,"f3":2377,"f4":false,"f5":"","f6":1}]
【时间】: 2022-04-14 16:35:28
【serverIp】: 172.18.139.x
【clientIp】: 172.18.139.x
【message】: xxx不能为空
【cause】: null
【StackTrace】: com.xxx.common.component.exception.ApiException: xxx不能为空
完美~