【问题标题】:Logback SMTPAppender send only one email at a particular time with all the exceptionsLogback SMTPAppender 在特定时间只发送一封电子邮件,所有例外情况
【发布时间】:2015-06-24 11:07:19
【问题描述】:

有没有办法在 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>

【问题讨论】:

    标签: java logging logback smtpappender


    【解决方案1】:

    一个简单的解决方案是将这些错误记录到一个文件中,并在您的服务器/机器上安装一个脚本,该脚本每天读取一次文件并发送电子邮件。

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

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

    一个基本的实现可能看起来像下面的类(我没有测试它 - 我使用的是 Java 8 语法,但如果需要,你可以用匿名类替换它)。请注意,我只保留导致异常的事件,您可能还希望在 sendBuffer 方法中保留 CyclicBuffer 的内容和/或在 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; }
    }
    

    您的 logback 配置文件如下所示:

    <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>
    

    更进一步,您可以添加参数,例如发送邮件的时间、每天的电子邮件数量等。

    【讨论】:

    • 非常感谢您的回复,很好的回答 - 但是我收到以下运行时异常。 ch.qos.logback.core.util.DynamicClassLoadingException: Failed to instantiate type lloyds.utils.ScheduledSMTPAppender 后跟 java.lang.InstantiationException 由于 ch.qos.logback.core.util.OptionHelper.instantiateByClassNameAndParameter。有什么想法吗?
    • @Carlos 你能显示整个堆栈跟踪吗(也许在一个新问题中让它更容易阅读)?它是否在某处说“由 ClassNotFoundException 引起”(在这种情况下,可能是重建 jar 或添加缺少的依赖项)?
    • 感谢 assylias - 将课程设置为 abstract 确实是问题所在。再次感谢!
    • @assylias 我的 HTML 布局内容在此之后为空,我该如何发送日期...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-13
    相关资源
    最近更新 更多