java - 自定义 SMTPAppender 中的 scheduleAtFixedRate 不会延迟

标签 java multithreading spring-boot scheduler smtpappender

当出现异常时,我将对象发送到服务器(顺序大约为 90 个),例如端点关闭,Logback 触发自定义 STMP appender,它只发送 1 封电子邮件,最大值为 1。 10 个日志,然后有延迟,其他日志被发送。 start() 是否有可能被过于频繁地调用?

问题:
scheduler.scheduleAtFixedRate(this::sendEmail, 1, 1L, TimeUnit.HOURS);
执行一次后不延迟,直接执行下一次,很多邮件不到一个小时就发完了。

我必须更改什么才能使调度程序的延迟生效?

After debugging I notices that Attaching appender named [EMAIL] to Logger[ROOT] occured two times could that be the problem, if yes how can I fix that?

洪水图片(如您所见,一小时内发送了两封电子邮件...)
Floof

Logback.xml 配置:

<configuration debug="true">

<!-- Logging per console and per email -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <layout class="ch.qos.logback.classic.PatternLayout">
        <Pattern>
            <!-- sets the format of the output -->
            %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
        </Pattern>
    </layout>
</appender>

<appender name="EMAIL" class="com.konverto.phonebillasaj.appenders.ScheduledSMTPAppender">
    <subject>TESTING: %logger{20} - %m</subject>
    <layout class="ch.qos.logback.classic.html.HTMLLayout" />

    <smtpHost>smtp.xxx.net</smtpHost>
    <smtpPort>587</smtpPort>
    <STARTTLS>true</STARTTLS>
    <username>xxx@domain.net</username>
    <password>myPass</password>
    <to>yyy@domain.net</to>
    <from>xxx@domain.net</from>
    <maxMessages>10</maxMessages>

    <!-- for testing , comment in production, default 256 -->
    <cyclicBufferTracker class="ch.qos.logback.core.spi.CyclicBufferTracker">
        <!-- Send just one log entry per email, ready for a lot of emails if you put one. -->
        <bufferSize>1</bufferSize>
    </cyclicBufferTracker>

    <!-- for testing , comment in production, default asynchronousSending = true -->
    <asynchronousSending>false</asynchronousSending>
</appender>


<logger name="com.konverto.phonebillasaj" level="error" additivity="false">
    <appender-ref ref="EMAIL"/>
    <appender-ref ref="CONSOLE" />
</logger>

<root level="error">
    <appender-ref ref="EMAIL" />
    <appender-ref ref="CONSOLE" />
</root>

appender的代码:

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, 1L, TimeUnit.HOURS);
}

@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 String getContentType() {
    return layout.getContentType();
}

public void setMaxMessages(int maxMessages) {
    this.maxMessages = maxMessages;

}

最佳答案

好像是这个问题

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

此调度程序将为每个初始化的附加程序排队发送电子邮件任务。 您能否在每条日志行中记录对象的实例或某个 id,并且每次都调用 start ?这应该可以阐明场景。

需要说明的是——我尝试使用 Sysout 而不是记录器(并在 main 方法中初始化)来编写相同的代码,并且它按预期工作。这是代码

public class ScheduledSMTPAppender /*extends SMTPAppender*/ {

    private static 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 static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, tf);


    private final List<String> events = new ArrayList<>();

    private int maxMessages = 10;

    public static void main(String args[]) {
        ScheduledSMTPAppender app = new ScheduledSMTPAppender();
        System.out.println("started");
        Thread t = new Thread(() -> {
            try {
                scheduler.awaitTermination(1, TimeUnit.HOURS);
            } catch (Exception e) {
            }
        });
        System.out.println("awaiting termination");
        t.start();
        System.out.println("thread initiated");
        new Thread(() -> {
            // Thread to add events to check logs
            for (int i =0;i<10;i++)
            {app.addEvent("asd");
            try {Thread.sleep(1000);
            } catch(Exception e) {};}
        }).start();
    }


    public ScheduledSMTPAppender() {
        super();
        System.out.println("starting");
        scheduler.scheduleAtFixedRate(this::sendEmail, 1, 1L, TimeUnit.SECONDS);
    }

    /*
    public ScheduledSMTPAppender(EventEvaluator<String> eventEvaluator) {
        super(eventEvaluator);
    }

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

    //@Override
    protected void sendBuffer(List<String> cb, String lastEventObject) {
        //System.out.println("sending email");
        events.add(lastEventObject);

        if (events.size() > maxMessages) {
            sendEmail();
        }
        System.out.println(new Date() + "sent email" + lastEventObject);
    }

    //needs to be synchronized for thread safety
    private synchronized void sendEmail() {
        try {
            if (events.isEmpty()) {
                return;
            }
            String lastEvent = events.get(events.size() - 1);
            events.remove(events.size() - 1);
            List<String> cb;

            if (events.isEmpty()) {
                cb = new ArrayList<>(1);
            } else {
                cb = new ArrayList<>(events.size());
                for (String e : events) {
                    cb.add(e);
                }

            }
            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 String getContentType() {
//        return layout.getContentType();
//    }

    public void setMaxMessages(int maxMessages) {
        this.maxMessages = maxMessages;

    }

    public void addEvent(String s) {
        events.add(s);
    }
}

关于java - 自定义 SMTPAppender 中的 scheduleAtFixedRate 不会延迟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57216073/

相关文章:

java - 以编程方式获取 ScrollView 的子级

c++ - 如何放置线程数组的动态大小

java.lang.IllegalArgumentException : Parameter value [1604438222] did not match expected type [java. lang.Integer(不适用)

java - 无法获取 PSQLException 的错误消息

java - 如何解决maven循环问题?

java - Spring MVC 和 Jackson 映射不返回 json 中的根元素

java - 检索 java ee 5 中已定义角色的列表

c - OpenMP : Creating threads, 并行性,循环障碍

android - 异步任务 : why update UI Thread in doInbackground will meet error?

spring - 在 JSP 页面中使用 Spring MVC 时出现错误 500