实现使用到的技术MQ、微信相关组件
实现思路:
1.采用AOP或者全局异常捕获错误日志,转换成json投递到MQ
2.消费者进行获取错误日志,保存错误日志
3.采用主动上报形式,将错误日志推送给短信、邮件给开发
4.Web后台展示错误日志
和ELK对比的优点
有可视化界面查询日志会更直观一些
注:只是单纯提供了一个思路,具体情况根据各自的完善需求进行优化等
存在的优化点?
1.一直报警插入错误日志问题?
思路:使用借口限流次数控制或者其他方案
表结构
CREATE TABLE `error_log` (
`messageId` varchar(255) NOT NULL,
`serviceId` varchar(255) DEFAULT NULL COMMENT '服务名称',
`ip` varchar(255) DEFAULT NULL,
`method_name` varchar(255) DEFAULT NULL COMMENT '方法名',
`param_content` varchar(255) DEFAULT NULL COMMENT '参数vvv',
`error_content` varchar(255) DEFAULT NULL,
`line_number` int(10) DEFAULT NULL COMMENT '行数',
`create_time` datetime DEFAULT NULL,
PRIMARY KEY (`messageId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
全局异常捕捉
package com.demo.exception;
import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson.JSON;
import com.demo.common.BaseRes;
import com.demo.config.RabbitConfig;
import com.demo.pojo.ErrorLog;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.Map;
import java.util.Set;
/**
* 全局异常处理
* 根据全局异常 通过mq保存异常日志
*/
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@Value("${server.port}")
private String port;
@Autowired
private RabbitTemplate rabbitTemplate;
@ResponseBody
@ExceptionHandler(Exception.class)
public String handleException(HttpServletRequest request, Exception e) throws UnknownHostException {
// 记录错误信息
ErrorLog errorLog = new ErrorLog();
errorLog.setMessageid(UUID.fastUUID().toString());
errorLog.setErrorContent(e.getMessage());
errorLog.setCreateTime(new Date());
errorLog.setIp(getIpPort());
errorLog.setParamContent(getRequestParam(request));
StackTraceElement stackTraceElement = e.getStackTrace()[0];
errorLog.setMethodName(stackTraceElement.getMethodName());
errorLog.setLineNumber(stackTraceElement.getLineNumber());
errorLog.setServiceid("demo");
send(JSON.toJSONString(errorLog));
return "系统错误";
}
/**
* 请求的参数
*
* @param request
* @return
*/
private String getRequestParam(HttpServletRequest request) {
//请求参数
Map<String, String[]> parameterMap = request.getParameterMap();
//获得map的key(就是表单中文本域的name集合)
Set<String> set = parameterMap.keySet();
for (String name : set) {
//获得每个文本域所对应的值
String[] vals = parameterMap.get(name);
System.out.print("name:" + name);
for (String val : vals) {
System.out.print(val + " ");
return val;
}
}
return null;
}
/**
* 发送消息
*
* @param
* @param message 消息
*/
public void send(String message) {
CorrelationData correlationId = new CorrelationData(UUID.fastUUID().toString());
rabbitTemplate.convertAndSend(RabbitConfig.EXCHANGE, RabbitConfig.ROUTINGKEY1,
message, correlationId);
}
/**
* 获取端口号
*
* @return
* @throws UnknownHostException
*/
private String getIpPort() throws UnknownHostException {
InetAddress localHost = Inet4Address.getLocalHost();
return localHost.getHostAddress() + ":" + port;
}
}
消费者监听消息
package com.demo.listener;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.demo.pojo.ErrorLog;
import com.demo.pojo.WxUser;
import com.demo.service.ErrorLogService;
import com.demo.service.WxUserService;
import com.demo.util.JwtUtils;
import com.demo.util.WeChatTemplateMsgUtils;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 消息消费者
*
*/
@Component
public class FirstConsumer {
@Autowired
private ErrorLogService errorLogService;
@Autowired
private WeChatTemplateMsgUtils weChatTemplateMsgUtils;
@Autowired
private WxUserService wxUserService;
//@RabbitListener(queues = {"first-queue","second-queue"})
@RabbitListener(queues = {"queue1"})
public void handleMessage(String message) {
// 处理消息
System.out.println("MQ监听消息 {} :"+message);
ErrorLog errorLog = JSON.parseObject(message, ErrorLog.class);
errorLogService.insertSelective(errorLog);
//进行上报发邮件、短息、微信公众号短信 等
List<WxUser> listWxUser = wxUserService.getListWxUser();
if (CollectionUtil.isNotEmpty(listWxUser)){
for (int i = 0; i <listWxUser.size() ; i++) {
WxUser wxUser = listWxUser.get(i);
weChatTemplateMsgUtils.manuscriptExamine(wxUser.getUsername(),errorLog);
}
}
}
}
短信模板
package com.demo.util;
import com.demo.pojo.ErrorLog;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.template.WxMpTemplate;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateIndustry;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
/**
* <pre>
* @Description: 微信模板消息推送实现
* @Aouth:
* @Date:
* </pre>
*/
@Slf4j
@Component
public class WeChatTemplateMsgUtils {
@Resource
private WxMpService wxMpService;
@Value("${wx.templateId}")
private String wx_template_id;
/**
* 发送模板消息
* @param openId
* @param errorLog
* @return
*/
public String manuscriptExamine(String openId, ErrorLog errorLog) {
//实例化模板对象
WxMpTemplateMessage wxMpTemplateMessage = new WxMpTemplateMessage();
//设置模板ID
wxMpTemplateMessage.setTemplateId(wx_template_id);
//设置发送给哪个用户
wxMpTemplateMessage.setToUser(openId);
//构建消息格式
List<WxMpTemplateData> listData = Arrays.asList(
new WxMpTemplateData("first", errorLog.getIp()+"服务错误报警", "#173177"),
new WxMpTemplateData("keyword1", errorLog.getMethodName(), "#173177"),
new WxMpTemplateData("keyword2", errorLog.getParamContent(), "#173177"),
new WxMpTemplateData("keyword3", errorLog.getErrorContent(), "#173177"),
new WxMpTemplateData("keyword4", errorLog.getLineNumber().toString(), "#173177"),
new WxMpTemplateData("remark", "如有疑问,请联系客服电话", "#173177")
);
//接收发送模板消息结果,就是msgid,if(msgid! = null)即成功
String wxTemplateResult = null;
//放进模板对象。准备发送
wxMpTemplateMessage.setData(listData);
try {
//发送模板
wxTemplateResult = wxMpService.getTemplateMsgService().sendTemplateMsg(wxMpTemplateMessage);
} catch (WxErrorException e) {
log.error("发送模板消息异常:{}", e.getMessage());
e.printStackTrace();
}
return wxTemplateResult;
}
}