AsyncUtil { public static ThreadPoolExecutor loggingExecutor( int corePoolSize, int maxPoolSize, BlockingQueue<Runnable> workQueue, String nameFormat, final Logger logger) { return new ThreadPoolExecutor( corePoolSize, maxPoolSize, 0L, TimeUnit.MILLISECONDS, workQueue, new ThreadFactoryBuilder().setDaemon(true).setNameFormat(nameFormat).build()) { @Override protected void afterExecute(Runnable runnable, Throwable throwable) { super.afterExecute(runnable, throwable); evaluateResult(runnable, throwable, logger); } }; } private  AsyncUtil(); static ScheduledThreadPoolExecutor loggingScheduledExecutor(
      int poolSize,
      String nameFormat,
      final Logger logger); static ScheduledThreadPoolExecutor singleThreadLoggingScheduledExecutor(
      String nameFormat,
      Logger logger); static ThreadPoolExecutor loggingExecutor(
      int corePoolSize,
      int maxPoolSize,
      BlockingQueue<Runnable> workQueue,
      String nameFormat,
      final Logger logger); static Runnable shutdownOnError(
      Lifecycle lifecycle,
      Logger logger,
      String message,
      Runnable runnable);  }
@Test public void testExecuteLogging() throws Exception { expectLogging(); control.replay(); ThreadPoolExecutor executor = AsyncUtil.loggingExecutor(1, 1, new LinkedBlockingQueue<>(), NAME_FORMAT, logger); executor.execute(() -> { throw new IllegalArgumentException("Expected exception."); }); latch.await(); }