首页 > 其他分享 >springboot使用log4j监控日志发送邮件

springboot使用log4j监控日志发送邮件

时间:2024-05-11 15:19:35浏览次数:15  
标签:springboot log4j appender 发送 MAIL 日志 event 邮件

实现log4j发送邮件功能大致流程:

  1、开启邮箱SMTP服务,获取SMTP登录密码

  2、引入javax.mail、javax.activation依赖

  3、配置log4j文件,指定邮件发送方和接收方以及发送方账号密码等

  4、重写SMTPAppender(不重写也能实现邮件发送功能)

开启邮箱SMTP服务

  这里以qq邮箱为例,首先进入账号管理界面,设置-账号

 

  

   找到POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务,绑定手机号即可开启服务,开启服务后会给你一串字符,这个字符就是后边配置SMTP邮箱的邮箱密码

  

 

引入javax.mail、javax.activation依赖

  

<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>

 配置log4j文件

### 配置根 ###(直接加上MAIl,以,分隔,不要删除原来的配置以免影响日志输出)
log4j.rootLogger = INFO,console,MAIL

  注意 :以下配置是添加,不要删除原来的配置

### 配置输出到邮件 ###
# log4j的邮件发送appender,这里如果重写了SMTPAppender就写自己重写过的appender
#log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender(默认的)
# 自己重写的appender
log4j.appender.MAIL=com.tian.api.ASMTPAppender.ASMTPAppender
#发送邮件的门槛,仅当等于或高于ERROR(比如FATAL)时,邮件才被发送 等级由低到高:debug<info<warn<Error<Fatal;
log4j.appender.MAIL.Threshold=ERROR
#日志发送邮件时关心的包(这里是自己添加的参数,如果不重写SMTPAppender不需要该参数,参数名可以自己命名)
log4j.appender.MAIL.includePackage=com.xxx.xxx.controller
#邮件缓冲区大小
log4j.appender.MAIL.BufferSize=16
#smtp服务地址(这里以qq为例,如果是163或者其他邮箱就换成其他邮箱的SMTP地址)
log4j.appender.MAIL.SMTPHost=smtp.qq.com
#端口(qq平台的端口一定要写587,其他的会卡住;163邮箱的尝试过写465或者587但是都会卡住)
log4j.appender.MAIL.SMTPPort=587
#邮件标题
log4j.appender.MAIL.Subject=ErrorMessage
#发件邮箱
[email protected]
#收件邮箱
[email protected]
#发件箱登陆用户名(与发件邮箱一致)
[email protected]
#发件箱登陆密码(这里在邮箱开启SMTP服务后会给你一串码)
log4j.appender.MAIL.SMTPPassword=123123123
#是否打印调试信息,如果选true,则会输出和SMTP之间的握手等详细信息
log4j.appender.MAIL.SMTPDebug=true
#邮件主题
log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout
log4j.appender.MAIL.layout.ConversionPattern= %d{yyyy-MM-dd HH:mm:ss.SSS} %5l [%5p] - %m%n

  !!!配置到这里就可以发送邮件啦。

重写SMTPAppender

  1、异步发送

  默认的SMTPAppender是同步发送邮件的,就是说如果检测到日志打印了告警级别的日志,业务代码会等待邮件发送完毕再执行,执行效果如下:

 

  

 

  所以最好改造一下进行异步发送,重写的appender如下:

 

public class ASMTPAppender extends SMTPAppender implements UnrecognizedElementHandler {

    ExecutorService fixedThreadPool = new ThreadPoolExecutor(2, 5, 3,
        TimeUnit.SECONDS, new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy());
    @Override
    protected void sendBuffer() {
        try {
            String s = this.formatBody();
            boolean allAscii = true;

            for(int i = 0; i < s.length() && allAscii; ++i) {
                allAscii = s.charAt(i) <= 127;
            }

            MimeBodyPart part;
            if (allAscii) {
                part = new MimeBodyPart();
                part.setContent(s, this.layout.getContentType());
            } else {
                try {
                    ByteArrayOutputStream os = new ByteArrayOutputStream();
                    Writer writer = new OutputStreamWriter(MimeUtility.encode(os, "quoted-printable"), "UTF-8");
                    writer.write(s);
                    writer.close();
                    InternetHeaders headers = new InternetHeaders();
                    headers.setHeader("Content-Type", this.layout.getContentType() + "; charset=UTF-8");
                    headers.setHeader("Content-Transfer-Encoding", "quoted-printable");
                    part = new MimeBodyPart(headers, os.toByteArray());
                } catch (Exception var7) {
                    StringBuffer sbuf = new StringBuffer(s);

                    for(int i = 0; i < sbuf.length(); ++i) {
                        if (sbuf.charAt(i) >= 128) {
                            sbuf.setCharAt(i, '?');
                        }
                    }

                    part = new MimeBodyPart();
                    part.setContent(sbuf.toString(), this.layout.getContentType());
                }
            }

            Multipart mp = new MimeMultipart();
            mp.addBodyPart(part);
            this.msg.setContent(mp);
            this.msg.setSentDate(new Date());
            // 使用线程池异步发送邮件
            fixedThreadPool.execute(() -> {
                try {
                    Transport.send(this.msg);
                } catch (MessagingException var6) {
                    LogLog.error("Error occured while sending e-mail notification.", var6);
                }
            });
        } catch (MessagingException var8) {
            LogLog.error("Error occured while sending e-mail notification.", var8);
        } catch (RuntimeException var9) {
            LogLog.error("Error occured while sending e-mail notification.", var9);
        }

    }

}

 

  改造后的执行效果如下:

 

  

 

  2、过滤监控文件

  默认的SMTPAppender会发送所有高于log4j配置文件中log4j.appender.MAIL.Threshold属性的日志,源码见org.apache.log4j.AppenderSkeleton

 

  public synchronized void doAppend(LoggingEvent event) {
        if (this.closed) {
            LogLog.error("Attempted to append to closed appender named [" + this.name + "].");
        } else if (this.isAsSevereAsThreshold(event.getLevel())) {
            Filter f = this.headFilter;

            while(true) {
                if (f != null) {
                    switch (f.decide(event)) {
                        case -1:
                            return;
                        case 0:
                            f = f.getNext();
                            continue;
                        case 1:
                            break;
                        default:
                            continue;
                    }
                }

                this.append(event);
                return;
            }
        }
    }
  // 这个方法会根据log4j配置文件中写入的优先级进行判断,大于等于写入的优先级才会发送邮件 public boolean isAsSevereAsThreshold(Priority priority) { return this.threshold == null || priority.isGreaterOrEqual(this.threshold); }

 

  如果我们想监控某一个或者某几个包下的文件日志情况,可以重写append和checkEntryConditions方法对判断条件进行修改

  可以在append方法调用checkEntryConditions的时候传入LoggingEvent

 

    public void append(LoggingEvent event) {
        if (this.checkEntryConditions(event)) {
            event.getThreadName();
            event.getNDC();
            event.getMDCCopy();
            if (this.locationInfo) {
                event.getLocationInformation();
            }

            event.getRenderedMessage();
            event.getThrowableStrRep();
            this.cb.add(event);
            if (this.evaluator.isTriggeringEvent(event)) {
                this.sendBuffer();
            }

        }
    }

  

  对LoggingEvent中的属性进行判断,如果不符合条件不进行邮件内容的组装和发送

  

    protected boolean checkEntryConditions(LoggingEvent event) {
        // 判断是否包含指定包名,这里的includePackage需要在重写的appender中定义并提供getter和setter方法,includePackage参数与log4j.appender.MAIL.includePackage属性对应
        if (includePackage != null && includePackage.contains(event.getLoggerName())) {
            return false;
        }
        if (this.msg == null) {
            this.errorHandler.error("Message object not configured.");
            return false;
        } else if (this.evaluator == null) {
            this.errorHandler.error("No TriggeringEventEvaluator is set for appender [" + this.name + "].");
            return false;
        } else if (this.layout == null) {
            this.errorHandler.error("No layout set for appender named [" + this.name + "].");
            return false;
        } else {
            return true;
        }
    }

  写在最后:个人认为在日志中耦合邮件的发送功能并不是一个好的选择,本文只是提供一个实现方案,有如需要可以选择skywalking等监控平台进行日志的监控和邮件的发送。

 

标签:springboot,log4j,appender,发送,MAIL,日志,event,邮件
From: https://www.cnblogs.com/beiweixiaotian/p/18186561

相关文章

  • springboot项目服务器访问速度慢-可能的解决方法
    springboot项目部署在服务器后,访问很慢或无法访问问题使用宝塔界面,尝试将打包后的jar包部署在服务器上运行时,发现访问速度不是很快,或者是直接访问不到。访问不到对应端口的springboot服务80端口也无法访问首次访问加载慢的解决方法修改jre文件找到安装的jre目......
  • 非常完整的开源无刷电机驱动项目+仅1300行代码的C语言异步网络库+简单到傻瓜都会用的
    1、VESC-非常完整的开源无刷电机驱动项目ESC是ElectricSpeedController的缩写,也就是电子调速控制器,简称电调;项目作者是BenjaminVedder,所以叫VESC,就是本杰明电调。这个项目主要分为几个部分,VESC固件,物料清单,VESC硬件,VESC工具软件,是一个非常完整的软硬件项目,并且配套的软......
  • Springboot Data Jdbc中Contains和Containing的用法
    Contains和Containing的用法privateStringtitle;privateList<String>tags;//查询标题包含指定字符串的书籍List<Book>findByTitleContains(Stringtitle);//查询包含指定标签的书籍List<Book>findByTagsContaining(Stringtag);--查询标题包含"Spring"的书......
  • 死磕nginx系列-nginx日志配置
    nginxaccess日志配置access_log日志配置access_log用来定义日志级别,日志位置。语法如下:日志级别: debug>info>notice>warn>error>crit>alert>emerg语法格式: access_logpath[format[buffer=size][gzip[=level]][flush=time][if=condition]];......
  • SpringBoot整合Mybatis时mapper文件和xml文件的位置
    xml文件放在resources下看下我的项目目录2.由于放在resurces下就无法扫描到xml文件,所以就需要在配置文件配置--mapper文件位置mybatis.mapper-locations=classpath:mapper/*.xml或mybatis.mapper-locations=classpath:/mapper/*.xmlxml和mapper文件放在一起我的项目......
  • Springboot单机多副本运行,解决端口冲突
    一、代码方式(修改配置类)@BeanpublicWebServerFactoryCustomizer<ConfigurableWebServerFactory>MyCustomizer(){returnnewWebServerFactoryCustomizer<ConfigurableWebServerFactory>(){@Overridepublicvoidcustomize(ConfigurableWebSer......
  • 日志的实现
    在mybatis-config.xml中可以配置日志的实现STDOUT_LOGGING标准日志的实现,比较简单<settings><settingname="logImpl"value="STDOUT_LOGGING"/></settings>LOG4J常用,可以进行一些自定义的配置导入log4j的包<dependency><groupId>lo......
  • springboot+vue快速部署前后台项目,无需服务器
    问题前言我们都知道,现在的主流开发大多数为,前后端分离,目前流行的框架,大多数是springboot+elementui这些框架,这无疑是给开发部署项目带来了便利,我们后台开发无需关心前端如何部署的,前端同样也无需关系后台如何部署,只需要确认能够访问即可。存在有如下问题:当由于条件有限,我......
  • 系统开发日志
    goget-u-vgoogle.golang.org/grpc确保你的Go环境已经安装并配置。安装gomobile工具:goget-u-vgoogle.golang.org/grpc创建Go代码,例如: packagemainimport("fmt""gomobile")funcmain(){gomobile.XamarinCallback("He......
  • SpringBoot集成微信APP支付
    目录废话不多说,先上代码上业务代码建议自己也看看官方文档,光C解决不了小问题。懂的吧代码随着微信支付的迭代可能会有啥问题,及时评论!废话不多说,先上代码application-dev.yml#支付pay:#支付回调通知地址notifyUrl:别想C填自己的#微信App支付weChat:别想C......