Spring Boot项目监控异常,发送邮件

Spring Boot项目监控异常,发送邮件

  • 需求
  • 实现
  • 打完收工!


需求

之前博客有提到,就是需要监控程序异常,因为这个是后台运行,无法监控程序异常,所以需要监控应用异常是否出现大面积报错。

应用每天记录报错次数,如果大于预定次数,则发送邮件通知团队处理,发送之后就不需要进行记录了,当天不需要进行通知了,隔天再进行通知。


实现

直接上代码,比较简单,记录一下:

@Service
@Slf4j
@EnableConfigurationProperties(MonitorConfig.class)
public class MonitorExceptionService {

    @Resource
    private JavaMailSender mailSender;

    @Resource
    private MonitorConfig monitorConfig;

    @Value("${spring.profiles.active:test}")
    private String profile;

    private volatile LocalDate lastDate;
    private volatile int errorTimes = 0;
    private volatile boolean mailFlag = false;
    private final Map<String, List<String>> ERROR_STACK = new ConcurrentHashMap<>();

    @Async
    synchronized public void recordError(Exception e) {
        LocalDate now = LocalDate.now(ZoneId.of("Asia/Shanghai"));
        if (Objects.isNull(lastDate) || now.isAfter(lastDate)) {
            lastDate = now;
            errorTimes = 0;
            mailFlag = false;
            ERROR_STACK.clear();
            log.info("clear old data done!");
        }
        if (mailFlag) {
            return;
        }
        errorTimes ++;

        String exceptionName = e.getClass().getName();
        List<String> list = ERROR_STACK.get(exceptionName);
        if (Objects.isNull(list)) {
            list = new ArrayList<>();
            ERROR_STACK.put(exceptionName, list);
        }
        list.add(e.getMessage());

        if (errorTimes >= monitorConfig.getMaxErrorTimes()) {
            try {
                sendEmail();
            } catch (MessagingException ex) {
                log.error("send mail notification error", ex);
            }
        }

    }

    private void sendEmail() throws MessagingException {
        log.info("start send email");
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage,true, StandardCharsets.UTF_8.name());
        messageHelper.setFrom(monitorConfig.getMailFrom());
        messageHelper.setTo(monitorConfig.getMailTo());
        messageHelper.setSubject(buildSubject());
        messageHelper.setText(monitorConfig.getMailTemplate());
        messageHelper.addAttachment("error.txt", new ByteArrayDataSource(buildAttachment(), "application/octet-stream"));
        mailSender.send(mimeMessage);
        log.info("end send email");
        mailFlag = true;
    }

    private byte[] buildAttachment() {
        StringBuilder sb = new StringBuilder();
        ERROR_STACK.forEach((err, list) -> {
            sb.append(err).append(": \n");

            for (int i = 0; i < list.size(); i++) {
                sb.append("   ").append(i).append(". ").append(list.get(i)).append("\n");
            }
            sb.append("\n");
        });

        return sb.toString().getBytes(StandardCharsets.UTF_8);
    }

    private String buildSubject() {
        String format = LocalDate.now(ZoneId.of("Asia/Shanghai")).format(DateTimeFormatter.ISO_LOCAL_DATE);

        return String.format("SYSTEM ERROR at %s (%s)",profile, format);
    }
}

程序是异步处理,但是需要上锁,因为是单节点,只需要这一个就够了,双节点问题也不大,就是发送两次而已,也可以换成分布式锁,没有条件的话可以换成数据库的锁即可。

打完收工!

你可能感兴趣的:(工作记录,spring,boot,spring,boot,异常监控)