分布式错误日志主动报警功能

 

实现使用到的技术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;
    }

   

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值