Spring Aop 实现线上异常邮件通知

公司的一个项目上线之后,领导提出需求:将线上的异常发送到开发运维人员的邮箱,及时处理bug。这种情况下必须用AOP啊,搞起

切面类的定义
@Component
@EnableAspectJAutoProxy
@Aspect
@Slf4j
public class SendExceptionMailAOPHandler {
    @Resource
    private EmailSendConfig emailSendConfig;  
    @Resource
    private NacosConfig Config;
    
    //创建切入点,在service层切入
    @Pointcut(value = "execution(* com.qoros.cvrm.provider.service.impl.*.*(..))")
    public void servicePointCut() {
    }
		
    @AfterThrowing(value = "servicePointCut()", throwing = "e")
    public void sendExceptionByMail(Exception e) {
        //获取异常信息
        InputStream inputStream = StreamUtils.getExceptionStream(e);
        try {
            String line = StreamUtils.getInputStreamFirstLine(inputStream);
            sendExceptionMail(inputStream, line);
        } finally {
            CloseUtils.closeQuietly(inputStream);
        }
		//发送邮件
		 public void sendExceptionMail(InputStream inputStream, String firstLine) {
        try {
        	//收件人列表
            String to[] = emailSendConfig.getExceptionInfoTo().split(",");
            //抄送人列表
            String cc[] = emailSendConfig.getExceptionInfoCC().split(",");
            //保存信息
            Map<String, InputStream> attachmentMap = new HashMap<>(1);
            //NacosConfig.getEnvType()获取系统的环境,dev ? uat? prd,在配置文件中定义的
            attachmentMap.put(NacosConfig.getEnvType() + "环境:CVRM异常文本.txt", inputStream);
            StringBuffer content = new StringBuffer();
            try {
            //由于项目部署在两台服务器上,需要拿到当前服务器IP
                Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
                NetworkInterface networkInterface;
                Enumeration<InetAddress> inetAddresses;
                InetAddress inetAddress;
                String ip;
                while (networkInterfaces.hasMoreElements()) {
                    networkInterface = networkInterfaces.nextElement();
                    inetAddresses = networkInterface.getInetAddresses();
                    while (inetAddresses.hasMoreElements()) {
                        inetAddress = inetAddresses.nextElement();
                        if (inetAddress != null && inetAddress instanceof Inet4Address) { // IPV4
                            ip = inetAddress.getHostAddress();
                            if(ip.indexOf(".")!=-1&&ip.indexOf("127.0.0.1")==-1) {
                                content.append("ip地址:").append(ip).append("<br/>");
                            }
                        }
                    }
                }
                content.append("具体异常信息,请参考附件文本内容");
            } catch (SocketException ex) {
                log.error("", ex);
            }
            //调用邮件配置类中的发送邮件方法
            emailSendConfig.sendMail(emailSendConfig.getEmailFrom(), to, cc, nacosConfig.getEnvType() + "环境:" + "系统异常提醒" + firstLine, content.toString(),
                    attachmentMap, "text/html;charset=UTF-8");
        } finally {
            CloseUtil.closeQuietly(inputStream);
        }
    }
		
    }
邮箱配置类的定义
@Configuration
@RefreshScope
@Slf4j
@Data
public class EmailSendConfig {
	//邮箱发送人信息,这个项目用nacos配置文件
    @Value("${mailbox.userName:}")
    private String emailAccount;
    @Value("${mailbox.password:}")
    private String emailPassword;
    @Value("${mailbox.host:}")
    private String emailHost;
    @Value("${mailbox.from:}")
    private String emailFrom;

	//异常接收人 
    @Value("${exceptionInfo.to:}")
    private String exceptionInfoTo;
    //抄送人
    @Value("${exceptionInfo.cc:}")
    private String exceptionInfoCC;
    private JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();

    private void initMailAccount() {
        javaMailSender.setHost(emailHost);
        javaMailSender.setUsername(emailAccount);
        javaMailSender.setPassword(emailPassword);
        javaMailSender.setDefaultEncoding("UTF-8");
    }

    public void sendMail(String emailFrom, String to[], String cc[], String subject, String text, Map<String, InputStream> attachmentMap, String attachmentMimeType) {
        log.info("--------------[mail/mailSend] start------------------");
        try {
            initMailAccount();
            MimeMessage message = javaMailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom(emailFrom, "XXXX系统");
            helper.setTo(to);
            helper.setCc(cc);
            helper.setSubject(subject);
            helper.setText(text, true);

            MimeMultipart mm = new MimeMultipart();
            message.setContent(mm);

            // 6. 创建文本"节点"
            MimeBodyPart textPart = new MimeBodyPart();
            textPart.setContent(text, "text/html;charset=UTF-8");
            mm.addBodyPart(textPart);
            if (MapUtils.isNotEmpty(attachmentMap)) {
                for (String attName : attachmentMap.keySet()) {
                    // 9. 创建附件"节点"
                    MimeBodyPart attachment = new MimeBodyPart();
                    InputStream inputStream = attachmentMap.get(attName);
                    // 读取本地文件
                    DataHandler dh2 = new DataHandler(new ByteArrayDataSource(inputStream, attachmentMimeType));
                    // 将附件数据添加到"节点"
                    attachment.setDataHandler(dh2);
                    // 设置附件的文件名(需要编码)
                    attachment.setFileName(attName);

                    mm.addBodyPart(attachment);     // 如果有多个附件,可以创建多个多次添加

                    inputStream.close();

                }
            }
            javaMailSender.send(message);
        } catch (Exception e) {
            log.error("邮件发送失败", e);
        }
        log.info("--------------[mail/mailSend] end------------------");
    }
}
StreamUtils类的定义,处理异常文件流
@Slf4j
public class StreamUtils {
/**
     * 异常对象转为输入流返回
     * @param e 异常
     * @return inputStream 一定要确保使用完后关闭,否则会造成内存泄漏
     */
    public static InputStream getExceptionStream(Exception e){
        //发送邮件
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024*2);
        InputStream inputStream = null;
        PrintStream printStream =null;
        try {
            printStream = new PrintStream(outputStream);
            e.printStackTrace(printStream);
            printStream.flush();
            outputStream.flush();
            inputStream = new ByteArrayInputStream(outputStream.toByteArray());
            inputStream.reset();
        } catch (IOException ex) {
            log.error("邮件发送异常日志", ex);
        } finally {
            closeQuietly(outputStream, printStream);
        }
        return inputStream;
    }
    //关闭资源的类
 	public static void closeQuietly(Closeable... closeable) {
        for (Closeable closeAb : closeable) {
            if (closeAb == null)
                continue;
            try {
                closeAb.close();
            } catch (IOException ex) {
                assert true;
            }
        }
    }
    
    }
写完之后测试ok,但在测试的过程中发现,遇到N次异常会发N封邮件,邮件999+,清理要花很长时间,得想办法继续优化一下。

在 SendExceptionMailAOPHandler 类中加一个缓存,发邮件的时候判断一下

 //用于存放异常类型 限制长度为1000,缓存清理时间为1小时
    Cache<String, Exception> cacheException = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder()
            .limit(1000).expireAfterWrite(1, TimeUnit.HOURS).buildCache();


    @AfterThrowing(value = "servicePointCut()", throwing = "e")
    public void sendExceptionByMail(Exception e) {
        //发送邮件
        InputStream inputStream = StreamUtils.getExceptionStream(e);
        try {
            String line = StreamUtils.getInputStreamFirstLine(inputStream);
            //如果该异常类是BusinessException(自定义业务异常)则每次都发送异常邮件
            //如果是其他异常类,则存放进cache缓存中,一小时之内不重复发送异常邮件
            if (e instanceof BusinessException || cacheException.get(line) == null) {
                //如果cacheException没有该异常则存放该异常
                cacheException.put(line, e);
                sendExceptionMail(inputStream, line);
            } else {
                return;
            }
        } finally {
            CloseUtils.closeQuietly(inputStream);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值