Log4j2 log ThreadPoolExecutor непослушное исключение - PullRequest
1 голос
/ 12 октября 2019

Как записать в журнал необработанные исключения из ThreadPoolExecutor?

Я пытался реализовать регистрацию в afterExecute (). Он может распечатать трассировку стека с помощью printStackTrace (), но не может войти в файл или консоль через log4j2.

Я не уверен, что это проблема log4j2 или моя проблема с ThreadPoolExecutor. Вот мой код:

package mytest;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class ThreadPoolTest {

    /** logger */
    private static Logger LOGGER = LogManager.getLogger(ThreadPoolTest .class);

    public static void main(String[] args) {
        final ThreadPoolExecutor executorService = CommonExecutors.newSingleThreadPoolExecutor(Executors.defaultThreadFactory());
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                LOGGER.debug("start...");
                String str = null;
                str.trim();
                LOGGER.debug("bye!");
            }
        });
        executorService.shutdown();
    }

    static class CommonExecutors extends ThreadPoolExecutor {

        public CommonExecutors( //
                int corePoolSize, //
                int maximumPoolSize, //
                long keepAliveTime, //
                TimeUnit unit, //
                BlockingQueue<Runnable> workQueue //
                ) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);

        }

        @Override
        public void afterExecute(Runnable r, Throwable t) {
            super.afterExecute(r, t);
            LOGGER.debug(t); // printed null, why?
            t.printStackTrace(); // can print the stack trace
        }

        /**
         * 
         * @param threadFactory
         * @return
         */
        public static ThreadPoolExecutor newSingleThreadPoolExecutor(ThreadFactory threadFactory) {
            return new CommonExecutors( //
                    1, //
                    1, //
                    0L, //
                    TimeUnit.MILLISECONDS, //
                    new LinkedBlockingQueue<Runnable>() //
            );
        }

    }
}

log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="DEBUG">
    <Appenders>
        <Console name="STDOUT" target="SYSTEM_OUT">
            <PatternLayout>
                <Pattern>%d [%t] %p %c{1} - %m %ex%n</Pattern>
            </PatternLayout>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="debug">
            <AppenderRef ref="STDOUT" level="DEBUG" />
        </Root>
    </Loggers>
</Configuration>

вывод:

2019-10-12 20:34:30,772 [pool-3-thread-1] DEBUG ThreadPoolTest - start... 
2019-10-12 20:34:30,774 [pool-3-thread-1] DEBUG ThreadPoolTest - null 
Exception in thread "pool-3-thread-1" 2019-10-12 20:34:30,776 pool-1-thread-1 DEBUG Stopping LoggerContext[name=1704856573, org.apache.logging.log4j.core.LoggerContext@2d8f65a4]
java.lang.NullPointerException
    at mytest.ThreadPoolTest.afterExecute(ThreadPoolTest.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1157)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

Ответы [ 2 ]

1 голос
/ 12 октября 2019

Ответ на ваш вопрос содержится в описании ThreadPoolExecutor.afterExecute () :

Примечание : когда действия включены в задачи (например,как FutureTask) либо явно, либо с помощью таких методов, как submit , эти объекты задачи перехватывают и поддерживают вычислительные исключения, поэтому они не вызывают внезапного завершения, а внутренние исключения не передаются этому методу.

Таким образом, вы должны либо отправить Runnable, используя

executorService.execute(new Runnable() {
    // ...
});

, либо использовать пример кода, предоставленный в документации ThreadPoolExecutor.afterExecute () , чтобы извлечь брошенныйисключение:

protected void afterExecute(Runnable r, Throwable t) {
  super.afterExecute(r, t);
  if (t == null && r instanceof Future<?>) {
    try {
      Object result = ((Future<?>) r).get();
    } catch (CancellationException ce) {
        t = ce;
    } catch (ExecutionException ee) {
        t = ee.getCause();
    } catch (InterruptedException ie) {
        Thread.currentThread().interrupt(); // ignore/reset
    }
  }
  if (t != null)
    System.out.println(t);
}
0 голосов
/ 12 октября 2019

Это фактически ожидаемое поведение. Цитирование метода JavaDoc показывает, почему это происходит:

Примечание : когда действия заключены в задачи (например, FutureTask) либо явно, либо с помощью таких методов, какв порядке отправки эти объекты задачи перехватывают и поддерживают вычислительные исключения, поэтому они не вызывают резкого завершения, и внутренние исключения не передаются этому методу.

В вашем случае NullPointerException не являетсяне исключение, которое привело к неожиданному завершению поставленной задачи. Таким образом, основываясь на поведении метода, исключение никогда не будет передано в метод afterExecute (отсюда и выбрасываемое значение null).

Это даже легче понять и понять, если вы отлаживаете код и проверяетесодержание Runnable в методе. При этом вы увидите, что результат FutureTask будет NullPointerException.

Я полагаю, вы можете перехватить результат задания и записать его, если вам действительно нужно.

...