草庐IT

java - Logback SMTPAppender 在特定时间只发送一封电子邮件,但有所有异常(exception)情况

coder 2024-03-06 原文

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

最佳答案

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

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

  • 扩展 SMTPAppender
  • 覆盖 SMTPAppenderBase 中的 sendBuffer 方法,以便它简单地将日志消息添加到集合中
  • ScheduledExecutorService 添加到每天运行一次 sendEmail 方法的 appender
  • 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>

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

关于java - Logback SMTPAppender 在特定时间只发送一封电子邮件,但有所有异常(exception)情况,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31024910/

有关java - Logback SMTPAppender 在特定时间只发送一封电子邮件,但有所有异常(exception)情况的更多相关文章

  1. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  2. ruby-on-rails - Ruby 检查日期时间是否为 iso8601 并保存 - 2

    我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby​​是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查

  3. ruby-on-rails - 将 Ruby 中的日期/时间格式化为 YYYY-MM-DD HH :MM:SS - 2

    这个问题在这里已经有了答案:Railsformattingdate(4个答案)关闭4年前。我想格式化Time.Now函数以显示YYYY-MM-DDHH:MM:SS而不是:“2018-03-0909:47:19+0000”该函数需要放在时间中.现在功能。require‘roo’require‘roo-xls’require‘byebug’file_name=ARGV.first||“Template.xlsx”excel_file=Roo::Spreadsheet.open(“./#{file_name}“,extension::xlsx)xml=Nokogiri::XML::Build

  4. ruby - 查找字符串中的内容类型(数字、日期、时间、字符串等) - 2

    我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s

  5. jquery - 我的 jquery AJAX POST 请求无需发送 Authenticity Token (Rails) - 2

    rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送

  6. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  7. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  8. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  9. ruby - 使用 Ruby 通过 Outlook 发送消息的最简单方法是什么? - 2

    我的工作要求我为某些测试自动生成电子邮件。我一直在四处寻找,但未能找到可以快速实现的合理解决方案。它需要在outlook而不是其他邮件服务器中,因为我们有一些奇怪的身份验证规则,我们需要保存草稿而不是仅仅发送邮件的选项。显然win32ole可以做到这一点,但我找不到任何相当简单的例子。 最佳答案 假设存储了Outlook凭据并且您设置为自动登录到Outlook,WIN32OLE可以很好地完成此操作:require'win32ole'outlook=WIN32OLE.new('Outlook.Application')message=

  10. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

随机推荐