Logback SMTPAppender在特定时间仅发送一封电子邮件,所有例外情况 [英] Logback SMTPAppender send only one email at a particular time with all the exceptions

查看:116
本文介绍了Logback SMTPAppender在特定时间仅发送一封电子邮件,所有例外情况的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否可以在LogBack中配置SMTPAppender以满足以下条件?

  1. 将所有例外分组为一条消息
  2. 仅在发生异常时发送每日日志报告
  3. 在一天的特定时间只发送一次报告,并以一封电子邮件的形式发送.

我当前的实现还远远没有完成上述操作,但是目前,当发生异常时,它会发送3封电子邮件-异常消息,堆栈跟踪和缓冲区刷新.

<!-- Filter duplicate Log Messages - Very important for Email Reports -->
<turboFilter class="ch.qos.logback.classic.turbo.DuplicateMessageFilter">
    <AllowedRepetitions>1</AllowedRepetitions>
    <CacheSize>1000</CacheSize>
</turboFilter>

<!--
    ############################################################
            BASIC APPENDER
    ############################################################
-->

<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <pattern>%d{HH:mm:ss.SSS} %-55(%X{user} %level [%thread] %logger{20}) - %msg%n</pattern>
    </encoder>
</appender>


<!--
    ############################################################
            EMAIL APPENDER
    ############################################################
-->
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />

<appender name="Email" class="ch.qos.logback.classic.net.SMTPAppender">
    <smtpHost>SERVER</smtpHost>
    <smtpPort>PORT</smtpPort>
    <asynchronousSending>false</asynchronousSending>
    <from>SENDER</from>
    <to>RECIPIENT</to>
    <subject>SUBJECT</subject>

    <layout class="ch.qos.logback.classic.PatternLayout">
        <pattern>%d{HH:mm:ss.SSS} %-55(%X{user} %level [%thread] %logger{20}) - %msg%n</pattern>
    </layout>

</appender>

<!--
    ############################################################
            OTHER
    ############################################################
-->
<root level="INFO">
    <appender-ref ref="Console"/>
    <appender-ref ref="RollingFile"/>
    <appender-ref ref="Email"/>
</root>

解决方案

一种简单的解决方案是将这些错误记录到文件中,并在服务器/计算机上设置一个脚本,该脚本每天读取一次文件并发送电子邮件. /p>

如果您想使用附加程序,在我看来,您需要自己动手,因为我认为标准SMTPAppender不允许您每天发送一次电子邮件:

  • 扩展SMTPAppender
  • 覆盖SMTPAppenderBase中的sendBuffer方法,以便仅将日志消息添加到集合中
  • 向每天运行一次sendEmail方法的附加程序添加ScheduledExecutorService
  • sendEmail方法将在this上进行同步以确保线程安全,检查集合是否为空,发送包含所有错误的电子邮件并清除集合

一个基本的实现可能类似于下面的类(我尚未测试过-我使用的是Java 8语法,但是如果需要,您可以将其替换为匿名类).请注意,我只保留导致异常的事件,您可能还希望将CyclicBuffer的内容保留在sendBuffer方法中和/或在sendEmail方法的错误之间添加一些错误分隔符.这可能会变得非常复杂,需要根据您的要求进行微调.

public class ScheduledSMTPAppender extends SMTPAppender {
  private final ThreadFactory tf = r -> {
    Thread t = new Thread(r, "ScheduledSMTPAppender Thread");
    t.setDaemon(true); //make daemon or it will prevent your program to exit
    return t;
  };
  private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, tf);
  private final List<ILoggingEvent> events = new ArrayList<> ();

  private int maxMessages = 10;

  public ScheduledSMTPAppender() { super(); }
  public ScheduledSMTPAppender(EventEvaluator<ILoggingEvent> eventEvaluator) { super(eventEvaluator); }

  @Override public void start() {
    super.start();
    scheduler.scheduleAtFixedRate(this::sendEmail, 1, 1, TimeUnit.DAYS);
  }

  @Override protected void sendBuffer(CyclicBuffer<ILoggingEvent> cb, ILoggingEvent lastEventObject) {
    events.add(lastEventObject);
    if (events.size() > maxMessages) sendEmail();
  }

  //needs to be synchronized for thread safety
  private synchronized void sendEmail() {
    try {
      if (events.isEmpty()) return;
      ILoggingEvent lastEvent = events.get(events.size() - 1);
      events.remove(events.size() - 1);
      CyclicBuffer<ILoggingEvent> cb;
      if (events.isEmpty()) {
        cb = new CyclicBuffer<>(1);
      } else {
        cb = new CyclicBuffer<>(events.size());
        for (ILoggingEvent e : events) cb.add(e);
      }
      super.sendBuffer(cb, lastEvent);
      events.clear();
    } catch (Exception e) {
      //Important to have a catch all here or the scheduled task will die
      addError("Error occurred while sending e-mail notification.", e);
    }
  }

  //this allows to make "maxMessages" a parameter of your appender
  public int getMaxMessages() { return maxMessages; }
  public void setMaxMessages(int maxMessages) { this.maxMessages = maxMessages; }
}

您的登录配置文件如下所示:

<appender name="Email" class="your.package.ScheduledSMTPAppender">
    <smtpHost>SERVER</smtpHost>
    <smtpPort>PORT</smtpPort>
    <asynchronousSending>false</asynchronousSending>
    <from>SENDER</from>
    <to>RECIPIENT</to>
    <subject>SUBJECT</subject>
    <maxMessages>10</maxMessages>

    <layout class="ch.qos.logback.classic.PatternLayout">
        <pattern>%d{HH:mm:ss.SSS} %-55(%X{user} %level [%thread] %logger{20}) - %msg%n</pattern>
    </layout>
</appender>

要进一步,您可以添加参数,例如发送该消息的时间,每天的电子邮件数量等.

Is there a way to configure the SMTPAppender in LogBack to meet the following criteria?

  1. Group all the exceptions into one message
  2. Only send the daily log report if exceptions occurred
  3. Send the report only once, grouped in one email, at a particular time of the day.

My current implementation is far from doing the above, but currently it sends 3 emails when an exception occurs - the exception message, the stacktrace, and a flush of the buffer.

<!-- Filter duplicate Log Messages - Very important for Email Reports -->
<turboFilter class="ch.qos.logback.classic.turbo.DuplicateMessageFilter">
    <AllowedRepetitions>1</AllowedRepetitions>
    <CacheSize>1000</CacheSize>
</turboFilter>

<!--
    ############################################################
            BASIC APPENDER
    ############################################################
-->

<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <pattern>%d{HH:mm:ss.SSS} %-55(%X{user} %level [%thread] %logger{20}) - %msg%n</pattern>
    </encoder>
</appender>


<!--
    ############################################################
            EMAIL APPENDER
    ############################################################
-->
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />

<appender name="Email" class="ch.qos.logback.classic.net.SMTPAppender">
    <smtpHost>SERVER</smtpHost>
    <smtpPort>PORT</smtpPort>
    <asynchronousSending>false</asynchronousSending>
    <from>SENDER</from>
    <to>RECIPIENT</to>
    <subject>SUBJECT</subject>

    <layout class="ch.qos.logback.classic.PatternLayout">
        <pattern>%d{HH:mm:ss.SSS} %-55(%X{user} %level [%thread] %logger{20}) - %msg%n</pattern>
    </layout>

</appender>

<!--
    ############################################################
            OTHER
    ############################################################
-->
<root level="INFO">
    <appender-ref ref="Console"/>
    <appender-ref ref="RollingFile"/>
    <appender-ref ref="Email"/>
</root>

解决方案

One simple solution is to log those errors to a file and have a script on your server/machine that reads the file once a day and sends an email.

If you want to use an appender, it seems to me that you would need to roll your own as I don't think the standard SMTPAppender enables you to send emails once a day:

  • extend SMTPAppender
  • override the sendBuffer method that is in SMTPAppenderBase so that it simply adds the log message to a collection
  • add a ScheduledExecutorService to your appender that runs a sendEmail method once a day
  • the sendEmail method would synchronize on this for thread safety, check if the collection is empty, send an email with all the errors and clear the collection

A basic implementation could look like the class below (I have not tested it - I'm using Java 8 syntax but you can replace it by anonymous classes if required). Note that I only keep the event that caused the exception, you may also want to keep the content of the CyclicBuffer in the sendBuffer method and/or add some error separators between errors in the sendEmail method. This can become quite complex and is to be fine-tuned depending on your requirements.

public class ScheduledSMTPAppender extends SMTPAppender {
  private final ThreadFactory tf = r -> {
    Thread t = new Thread(r, "ScheduledSMTPAppender Thread");
    t.setDaemon(true); //make daemon or it will prevent your program to exit
    return t;
  };
  private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, tf);
  private final List<ILoggingEvent> events = new ArrayList<> ();

  private int maxMessages = 10;

  public ScheduledSMTPAppender() { super(); }
  public ScheduledSMTPAppender(EventEvaluator<ILoggingEvent> eventEvaluator) { super(eventEvaluator); }

  @Override public void start() {
    super.start();
    scheduler.scheduleAtFixedRate(this::sendEmail, 1, 1, TimeUnit.DAYS);
  }

  @Override protected void sendBuffer(CyclicBuffer<ILoggingEvent> cb, ILoggingEvent lastEventObject) {
    events.add(lastEventObject);
    if (events.size() > maxMessages) sendEmail();
  }

  //needs to be synchronized for thread safety
  private synchronized void sendEmail() {
    try {
      if (events.isEmpty()) return;
      ILoggingEvent lastEvent = events.get(events.size() - 1);
      events.remove(events.size() - 1);
      CyclicBuffer<ILoggingEvent> cb;
      if (events.isEmpty()) {
        cb = new CyclicBuffer<>(1);
      } else {
        cb = new CyclicBuffer<>(events.size());
        for (ILoggingEvent e : events) cb.add(e);
      }
      super.sendBuffer(cb, lastEvent);
      events.clear();
    } catch (Exception e) {
      //Important to have a catch all here or the scheduled task will die
      addError("Error occurred while sending e-mail notification.", e);
    }
  }

  //this allows to make "maxMessages" a parameter of your appender
  public int getMaxMessages() { return maxMessages; }
  public void setMaxMessages(int maxMessages) { this.maxMessages = maxMessages; }
}

Your logback configuration file then looks like:

<appender name="Email" class="your.package.ScheduledSMTPAppender">
    <smtpHost>SERVER</smtpHost>
    <smtpPort>PORT</smtpPort>
    <asynchronousSending>false</asynchronousSending>
    <from>SENDER</from>
    <to>RECIPIENT</to>
    <subject>SUBJECT</subject>
    <maxMessages>10</maxMessages>

    <layout class="ch.qos.logback.classic.PatternLayout">
        <pattern>%d{HH:mm:ss.SSS} %-55(%X{user} %level [%thread] %logger{20}) - %msg%n</pattern>
    </layout>
</appender>

To go further, you could add parameters, such as the time of the day when this is sent, the number of emails per day etc.

这篇关于Logback SMTPAppender在特定时间仅发送一封电子邮件,所有例外情况的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆