zoukankan      html  css  js  c++  java
  • springboot+rabbitMQ+Aspect发送特定业务的异常报告邮件

    一、需求描述: 系统中,如果有一些数据发生异常的情况需要及时处理掉。比如: 跟其他系统对接时候发生异常!

    二、思路:  利用切面捕获到某些特定业务接口产生的异常信息,然后 发送到MQ中,MQ获取到异常信息,发送到接收异常报告的邮箱。

    末尾有Github地址~

    代码部分 :

    三、自定义异常日志的注解

    主要作用是: 方便只对加上注解的特定业务接口起作用,其他地方不用。

    /**
     * 自定义异常日志的注解
     * @author 600336
     */
    @Documented
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.PARAMETER, ElementType.METHOD})
    public @interface ExceptionLogAnnotation {
        /**
         * 操作类型
         * @return
         */
        String operationType() default "";
        /**
         * 操作名称
         */
          String operationName() default "";
    }

    四、异常的切面

    @Aspect
    @Component
    public class ExceptionLogAspect {
    
        private static final Logger logger = LoggerFactory.getLogger(ExceptionLogAspect.class);
    
        @Autowired
        ExceptionLogSender exceptionLogSender;
    
        /**
         * 这里切点是web包 并且加了自定义注解(ExceptionLogAnnotation)
         */
        @Pointcut("execution(public * czs.web..*(..)) && @annotation(czs.annotation.ExceptionLogAnnotation)")
        public void exceptionLog() {
        }
    
        @AfterThrowing(pointcut = "exceptionLog()", throwing = "e")
        public void handleThrowing(JoinPoint joinPoint, Exception e) {
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            Object[] args = joinPoint.getArgs();
            logger.error("ExceptionLogAspect切面获取到异常:" + e.getMessage(), e);
    
            //开始打log
            //logger.error("异常:" + e.getMessage());
            //logger.error("异常所在类:" + className);
            // logger.error("异常所在方法:" + methodName);
            //logger.error("异常中的参数:");
            //logger.error(methodName);
    
            Class targetClass = null;
            String operationType = "";
            String operationName = "";
            Method[] methods = null;
            try {
                targetClass = Class.forName(className);
                methods = targetClass.getMethods();
            } catch (ClassNotFoundException e2) {
                e.printStackTrace();
            }
            if (methods != null) {
                for (Method method : methods) {
                    if (method.getName().equals(methodName)) {
                        Class[] clazzs = method.getParameterTypes();
                        if (clazzs != null && clazzs.length == args.length &&
                                method.getAnnotation(ExceptionLogAnnotation.class) != null) {
                            operationName = method.getAnnotation(ExceptionLogAnnotation.class).operationName();
                            operationType = method.getAnnotation(ExceptionLogAnnotation.class).operationType();
                            break;
                        }
                    }
                }
            }
    
            logger.info("operationName :" + operationName);
            logger.info("operationType :" + operationType);
    
            //异常收集的StringBuffer
            StringBuffer execBuff = new StringBuffer();
            //异常的具体信息
            String exceTrace = ExceptionUtils.getTrace(e);
            //拼接信息
            execBuff.append("[切面捕获异常信息]").append("<br/>")
                    .append("异常:").append(e.getMessage()).append("<br/>")
                    .append("异常所在类:").append(className).append("<br/>")
                    .append("异常所在方法:").append(methodName).append("<br/>")
                    .append("异常中的参数:").append(JSONObject.toJSONString(args)).append("<br/>")
                    .append("操作类型:").append(operationType).append("<br/>")
                    .append("操作名称:").append(operationName).append("<br/>")
                    .append("具体异常信息如下:").append("<br/>")
                    .append(exceTrace)
            ;
    
            //发消息
            exceptionLogSender.send(execBuff.toString());
    
        }
    
    
        /**
         * @param joinPoint
         * @throws Throwable
         */
        @Before("exceptionLog()")
        public void doBefore(JoinPoint joinPoint) throws Throwable {
            // 接收到请求,记录请求内容
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();         // 记录下请求内容
            logger.info("开始记录请求数据-------->>>>> ");
            logger.info("URL : " + request.getRequestURL().toString());
            logger.info("HTTP_METHOD : " + request.getMethod());
            logger.info("IP : " + request.getRemoteAddr());
            logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
            logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
        }
    
    }

    根据异常T获取异常信息的工具类ExceptionUtils方法:

        /**
         * 获取异常的信息
         *
         * @param t
         * @return
         */
        public static String getTrace(Throwable t) {
            StringWriter stringWriter = new StringWriter();
            PrintWriter writer = new PrintWriter(stringWriter);
            t.printStackTrace(writer);
            StringBuffer buffer = stringWriter.getBuffer();
            return buffer.toString();
        }

    六、rabbitmq配置、生产者和消费者

    @Configuration
    public class RabbitConfig {
    
        /*异常日志的队列*/
        @Bean
        public Queue exceptionLogQueue() {
            return new Queue("exception.log");
        }
    }
    /**
     * 单生产者-单消费者
     * 异常日志信息-生产者
     */
    @Component
    public class ExceptionLogSender {
    
        private static final Logger log = LoggerFactory.getLogger(ExceptionLogSender.class);
        @Autowired
        public AmqpTemplate amqpTemplate;
    
        public void send(String info) {
            log.info("生产者 发送异常日志信息:" + info);
            this.amqpTemplate.convertAndSend("exception.log", info);
        }
    
    
    }
    /**
     * 单生产者-单消费者
     * 异常日志信息-消费
     */
    @Component
    public class ExceptionLogReceiver {
        private static final Logger log = LoggerFactory.getLogger(ExceptionLogReceiver.class);
        @Autowired
        public AmqpTemplate amqpTemplate;
    
        //监听器监听指定的Queue
        @RabbitListener(queues = "exception.log")
        public void process(String info) {
            log.info("消费者 收到异常日志信息:" + info);
            //发送邮件
            try {
                SendMailUtil.sendMail("系统异常日志信息报告", info);
            } catch (Exception e) {
                log.error("发送异常日志信息邮件失败!",e);
            }
        }
    }

    七、发送邮件工具类

    public class SendMailUtil {
    
        /**
         * 发送邮件
         *
         * @param subject 主题
         * @param content 内容
         * @throws Exception 这里可以try catch一下异常~
         */
        public static void sendMail(String subject, String content) throws Exception {
    
            Properties props = new Properties();
            props.put("mail.smtp.auth", "true");
            props.put("mail.smtp.host", "smtp.exmail.qq.com");  //腾讯企业邮箱的smtp服务器
            props.put("mail.smtp.port", "465");
            props.put("mail.smtp.ssl.enable", "true");
    
    
            String from = "xxx1@qq.com";    // 发送方地址
            String to = "xxx2@qq.com";      // 收件方地址
            String username = "xxx1@qq.com";  //邮箱名称
            String password = "mima";       //邮箱密码
    
            List<String> recipients = Arrays.asList(to);
    
            //认证
            Authenticator smtpAuth = new PopupAuthenticator(from, password);
            Session session = Session.getDefaultInstance(props, smtpAuth);
    
            //上线关闭debug
            session.setDebug(true);
            //session.setDebug(false);
            MimeMessage message = new MimeMessage(session);
            message.setFrom(new InternetAddress(from));
    
            //多个收件人
            int num = recipients.size();
            InternetAddress[] rece_addresses = new InternetAddress[recipients.size()];
            for (int i = 0; i < num; i++) {
                rece_addresses[i] = new InternetAddress(recipients.get(i));
            }
            message.setRecipients(Message.RecipientType.TO, rece_addresses);
    
            message.setContent(content, "text/html; charset=utf-8");
            message.setSubject(subject);
            message.saveChanges();
    
            Transport transport = session.getTransport("smtp");
            transport.connect("smtp.exmail.qq.com", username, password);
            transport.sendMessage(message, message.getAllRecipients());
            transport.close();
        }
    
    
        public static void main(String[] args) throws Exception {
            sendMail("主题测试的", "hello");
        }
    }
    
    
    class PopupAuthenticator extends Authenticator {
        String username = null;
        String password = null;
    
        public PopupAuthenticator() {
        }
    
        public PopupAuthenticator(String username, String password) {
            this.username = username;
            this.password = password;
        }
    
        PasswordAuthentication performCheck(String user, String pass) {
            username = user;
            password = pass;
            return getPasswordAuthentication();
        }
    
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(username, password);
        }
    
    }

    八、测试接口( 项目名称我这里取rabbitmq,端口8888 )

    @Controller
    @RequestMapping("/exception")
    public class ExceptionController {
    
        /**
         * 测试地址:http://localhost:8888/rabbitmq/exception/test/10
         * 测试地址:http://localhost:8888/rabbitmq/exception/test/0
         * ***这里加了注解ExceptionLogAnnotation
         *
         * @param id
         * @return
         */
        @RequestMapping(value = "/test/{id}", method = RequestMethod.GET)
        @ResponseBody
        @ExceptionLogAnnotation(operationType = "测试操作", operationName = "测试异常或者测试返回")
        public JSONObject test(@PathVariable Integer id) {
            JSONObject result = new JSONObject();
            result.put("success", "true");
            result.put("test", "test");
            //id可以传0 测试异常
            int aa = 10 / id;
            return result;
        }
    
    
        /**
         * 测试地址:http://localhost:8888/rabbitmq/exception/test2/10
         * 测试地址:http://localhost:8888/rabbitmq/exception/test2/0
         * ***这里不加注解ExceptionLogAnnotation
         *
         * @param id
         * @return
         */
        @RequestMapping(value = "/test2/{id}", method = RequestMethod.GET)
        @ResponseBody
        public JSONObject test2(@PathVariable Integer id) {
    
            JSONObject result = new JSONObject();
            result.put("success", "true");
            result.put("test2", "test2");
            //id可以传0 测试异常
            int aa = 10 / id;
            return result;
        }
    
    }

    九、效果

    请求 http://localhost:8888/rabbitmq/exception/test/0 这个链接会发邮件给邮箱;
    其他的三个链接不会发邮件。

    十、总结

           1.rabbitmq需要自己装一下 

      2.有想法就行动

      3.多参考资料

      4.github地址: https://github.com/ColoZhu/springbootRabbitmqMail.git

  • 相关阅读:
    关于ThreadLocal的理解
    常用Linux软件安装
    Spring事务注解@Transactional失效的问题
    使用jackson转换xml格式数据进行响应
    创建简单web程序了解servlet
    JDBC
    StringBuild类
    Canlendar 日期类
    Java Date 时间类的使用
    QWeb
  • 原文地址:https://www.cnblogs.com/coloz/p/12668098.html
Copyright © 2011-2022 走看看