• 中文
    • English
  • 注册
  • 查看作者
  • SpringBoot项目异常发送邮件报警

    一. 前言

    公司的一些项目部署到线上后,有时候会因为bug等其他原因触发异常信息,而且没有做相关的项目监控,虽然做了日志处理,但是还是需要每天打开日志检查,非常的麻烦。由于大多数SpringBoot项目都做了全局的统一异常处理,所以我们可以在每次异常被触发的时候,给我们发送一个邮件来捕获异常信息。

    二.  配置Mail

    Spring Boot2.x的版本默认集成了mail模块,所以只需要在pom.xml中添加相关依赖即可

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>

    接下来在application.yml中添加相关配置,这里以163邮箱为例

    spring:
      mail:
        host: smtp.163.com  #SMTP服务器地址
        port:
        username: zhangjiaboke@163.com  # 发件的邮箱
        password: asdfdsfafaaxx #客户端授权密码,不是邮箱登录密码
        protocol: smtp  # SMTP服务器用到的协议
        default-encoding: UTF-8  # 消息包的编码
        #设置使用ssl连接时候才需要开启下面的内容配置TLS,安全起见,建议开启SSL,并添加以下配置
        properties:
          mail.smtp.auth: true
          mail.smtp.starttls.enable: true 
          mail.smtp.starttls.required: true
          mail.smtp.socketFactory.port: 465 
          mail.smtp.socketFactory.class: javax.net.ssl.SSLSocketFactory
          mail.smtp.socketFactory.fallback: false

    其中,host为你的发件邮件的SMTP服务器地址这里给出常用邮箱SMTP,port为端口号

    163邮箱:smtp.163.com , port不用写

    126邮箱:smtp.126.com, port不用写

    QQ邮箱:smtp.qq.com, port是587

    值得注意的是,password并不是邮箱的登录密码,而是客户端的授权密码,每种邮箱获取客户端授权密码的方式不同,可以自行查询,这里不再赘述。

    三. 编写发送邮箱工具类

    参考了Clement-Xu大佬的《spring boot发送邮件 》一文

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.core.io.FileSystemResource;
    import org.springframework.mail.SimpleMailMessage;
    import org.springframework.mail.javamail.JavaMailSender;
    import org.springframework.mail.javamail.MimeMessageHelper;
    import org.springframework.stereotype.Component;
    import org.springframework.stereotype.Service;
    
    import javax.mail.MessagingException;
    import javax.mail.internet.MimeMessage;
    import java.io.File;
    
    /**
     * @Author : ZhangJia
     * @Date : 2020/4/1 14:47
     * @Description : 
     */
    @Component
    public class MailUtil {
    
    
        private final Logger logger = LoggerFactory.getLogger(this.getClass());
    
        @Autowired
        private JavaMailSender sender;
    
        @Value("${spring.mail.username}")
        private String from;
    
        /**
         * 发送纯文本的简单邮件
         * @param to
         * @param subject
         * @param content
         */
        public void sendSimpleMail(String to, String subject, String content) {
            SimpleMailMessage message = new SimpleMailMessage();
            String[] toRecive = to.split(",");
            message.setFrom(from);
            message.setTo(toRecive);
            message.setSubject(subject);
            message.setText(content);
            message.setCc(from); //添加抄送人,否则会报Failed messages: com.sun.mail.smtp.SMTPSendFailedException: 554 DT:SPM 163 smt
    
            try {
                sender.send(message);
                logger.info("简单邮件已经发送。");
            } catch (Exception e) {
                logger.error("发送简单邮件时发生异常!", e);
            }
        }
    
        /**
         * 发送html格式的邮件
         * @param to
         * @param subject
         * @param content
         */
        public void sendHtmlMail(String to, String subject, String content) {
            MimeMessage message = sender.createMimeMessage();
    
            try {
                String[] toRecive = to.split(",");
                //true表示需要创建一个multipart message
                MimeMessageHelper helper = new MimeMessageHelper(message, true);
                helper.setFrom(from);
                helper.setTo(toRecive);
                helper.setSubject(subject);
                helper.setText(content, true);
    
                sender.send(message);
                logger.info("html邮件已经发送。");
            } catch (MessagingException e) {
                logger.error("发送html邮件时发生异常!", e);
            }
        }
    
        /**
         * 发送带附件的邮件
         * @param to
         * @param subject
         * @param content
         * @param filePath
         */
        public void sendAttachmentsMail(String to, String subject, String content, String filePath) {
            MimeMessage message = sender.createMimeMessage();
    
            try {
                String[] toRecive = to.split(",");
                //true表示需要创建一个multipart message
                MimeMessageHelper helper = new MimeMessageHelper(message, true);
                helper.setFrom(from);
                helper.setTo(toRecive);
                helper.setSubject(subject);
                helper.setText(content, true);
    
                FileSystemResource file = new FileSystemResource(new File(filePath));
                String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
                helper.addAttachment(fileName, file);
    
                sender.send(message);
                logger.info("带附件的邮件已经发送。");
            } catch (MessagingException e) {
                logger.error("发送带附件的邮件时发生异常!", e);
            }
        }
    
        /**
         * 发送嵌入静态资源(一般是图片)的邮件
         * @param to
         * @param subject
         * @param content 邮件内容,需要包括一个静态资源的id,比如:<img src=\"cid:rscId01\" >
         * @param rscPath 静态资源路径和文件名
         * @param rscId 静态资源id
         */
        public void sendInlineResourceMail(String to, String subject, String content, String rscPath, String rscId) {
            MimeMessage message = sender.createMimeMessage();
    
            try {
                String[] toRecive = to.split(",");
                //true表示需要创建一个multipart message
                MimeMessageHelper helper = new MimeMessageHelper(message, true);
                helper.setFrom(from);
                helper.setTo(toRecive);
                helper.setSubject(subject);
                helper.setText(content, true);
    
                FileSystemResource res = new FileSystemResource(new File(rscPath));
                helper.addInline(rscId, res);
    
                sender.send(message);
                logger.info("嵌入静态资源的邮件已经发送。");
            } catch (MessagingException e) {
                logger.error("发送嵌入静态资源的邮件时发生异常!", e);
            }
        }
    
    }

    四.  在全局异常类中发送错误信息

    关于全局异常类的配置和使用,请查看本站《SpringBoot:统一异常处理》一文,在统一异常处理类中,将报错信息发送至邮箱即可。

    mport io.zhangjia.springbootmd5.exception.TestException;
    import io.zhangjia.springbootmd5.util.Result;
    import org.springframework.util.StringUtils;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    @ControllerAdvice
    public class GlobalExceptionHandler {
        @Autowired
        private MailUtil mailUtil;
        @ExceptionHandler
        @ResponseBody
        public Result handle(Exception e) {
            if(e instanceof TestException) {
                TestException testException =  (TestException) e;
                int code = testException.getCode();
                String message = testException.getMessage();
                mailUtil.sendSimpleMail("zhangjia@188.com","自定义异常",message );
                return new Result(code,message);
            }
            String message = e.getMessage();
            if(StringUtils.isEmpty(message)){
                message = "未定义错误";
                
            }
            return new Result(-1,message);
        }
    }

    此时,当有异常被触发的时候,邮箱便可以收到相关通知:

    SpringBoot项目异常发送邮件报警

    五.  美化邮件

    在上面的例子中,我们采用了纯文本的方式发送邮件,不太美观,我们可以写个网页美化一下,采用Thymeleaf交互数据

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
    <head>
        <meta charset="UTF-8">
        <title>张甲博客:系统异常提示</title>
        <link rel='stylesheet' href='https://unpkg.com/ionicons@4.5.10-0/dist/css/ionicons.min.css'>
        <link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Montserrat:1000,700&amp;display=swap'>
        <style>
            body {
                background-color: #EBECF0;
            }
    
            #mail-body {
                display: flex;
                flex-direction: column;
                justify-content: space-between;
                align-items: center;
            }
    
            .mail-name {
                display: inline-block;
                width: 100px;
                height: 50px;
                line-height: 50px;
                background-color: #EBECF0;
                text-align: center;
                box-shadow: -5px -5px 20px #FFF, 5px 5px 20px #BABECC;
                border-radius: 8px;
            }
    
            .mail-value {
                display: inline-block;
                width: 400px;
                height: 50px;
                line-height: 50px;
                background-color: #EBECF0;
                margin-right: 8px;
                box-shadow: inset 2px 2px 5px #BABECC, inset -5px -5px 10px #FFF;
                box-sizing: border-box;
                transition: all 0.2s ease-in-out;
                appearance: none;
                -webkit-appearance: none;
                border-radius: 10px;
                margin-left: 20px;
                padding-left: 10px;
            }
    
            #mail-info {
                width: 535px;
                height: 100%;
                background-color: #EBECF0;
                margin-right: 8px;
                box-shadow: inset 2px 2px 5px #BABECC, inset -5px -5px 10px #FFF;
                box-sizing: border-box;
                transition: all 0.2s ease-in-out;
                appearance: none;
                -webkit-appearance: none;
                border-radius: 10px;
                padding: 10px;
                white-space: normal;
                word-break: break-all;
                overflow: hidden;
            }
    
            div {
                margin-bottom: 20px;
            }
        </style>
    </head>
    <body>
    
    <div id="mail-body">
    
        <div class="segment">
            <h1>Vchain 异常信息</h1>
        </div>
    
        <div>
                <span class="mail-name">
                    异常代码:
                </span>
    
                 <span class="mail-value" th:text="${code}">
                </span>
        </div>
    
        <div>
                <span class="mail-name">
                    异常信息:
                </span>
    
                 <span class="mail-value" th:text="${message}">
    
                </span>
        </div>
    
        <div>
                <span class="mail-name">
                    发生时间:
                </span>
    
               <span class="mail-value" th:text="${create}">
    
              </span>
        </div>
        <div id="mail-info" th:text="${info}">
    
        </div>
    </div>
    
    </div>
    </body>
    </html>

    此时将统一异常类修改为:

    package com.enjoysix.vchain.exception;
    
    import com.enjoysix.vchain.util.MailUtil;
    import com.enjoysix.vchain.util.Result;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.thymeleaf.TemplateEngine;
    import org.thymeleaf.context.Context;
    
    import java.io.ByteArrayOutputStream;
    import java.io.PrintStream;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @Author : ZhangJia
     * @Date : 2019/11/1 15:00
     * @Description : 统一异常处理类
     * 统一异常处理类思路:
     * 程序可能遇到的任何错误都在service层处理
     * (因为在Controller中调用了Service,所以Service发生的异常,也会被GlobalExceptionHandler统一处理)
     * 在service层可能发生错误的地方通过调用VchainException,传入异常类型,将异常抛出
     */
    @ControllerAdvice
    public class GlobalExceptionHandler {
        private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
        @Autowired
        private MailUtil mailUtil;
        @Autowired
        private TemplateEngine templateEngine;
    
    
        @ExceptionHandler
        @ResponseBody
        public Result handle(Exception e) {
            Result result;
            int code;
            String message;
            if (e instanceof VchainException) {  //如果是自定义异常
                VchainException vchainException = (VchainException) e;
                code = vchainException.getCode();
                message = vchainException.getMessage(); //Throwable类中的message
                result = new Result(code, message);  //设置code和result
            } else { //如果是其他异常,统一报未定义错误
                code = ExceptionCode.UNDEFINED_ERROR.getCode();
                message = ExceptionCode.UNDEFINED_ERROR.getMsg();
                result = new Result(code, message);
            }
    
    
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            e.printStackTrace(new PrintStream(byteArrayOutputStream));
            String exception = byteArrayOutputStream.toString();
    
            Map<String, Object> map = new HashMap<>();
            map.put("code", code);
            map.put("message", message);
            map.put("info", exception);
            map.put("create", new Date());
    
            Context context = new Context();
            context.setVariables(map);
            String emailContent = templateEngine.process("/mail", context);
            mailUtil.sendHtmlMail("zhangjia@188.com", "系统报警信息", emailContent);
            return result;
        }
    
    }

    当异常发生时,收到的邮件如下:

    SpringBoot项目异常发送邮件报警

  • 0
  • 1
  • 0
  • 3.2k
  • Tobehappyohzjmarina

    请登录之后再进行评论

    登录
  • 0
    L1
    111
  • 单栏布局 侧栏位置: