public static <T> T waitWithLogging(
        Logger log,
        String prefix,
        String action,
        CompletableFuture<T> future,
        Deadline deadline,
        Time time
    ) throws Throwable {
        log.info("{}Waiting for {}", prefix, action);
        try {
            T result = time.waitForFuture(future, deadline.nanoseconds());
            log.info("{}Finished waiting for {}", prefix, action);
            return result;
        } catch (TimeoutException t)  {
            log.error("{}Timed out while waiting for {}", prefix, action, t);
            TimeoutException timeout = new TimeoutException("Timed out while waiting for " + action);
            timeout.setStackTrace(t.getStackTrace());
            throw timeout;
        } catch (Throwable t)  {
            if (t instanceof ExecutionException) {
                ExecutionException executionException = (ExecutionException) t;
                t = executionException.getCause();
            }
            log.error("{}Received a fatal error while waiting for {}", prefix, action, t);
            throw new RuntimeException("Received a fatal error while waiting for " + action, t);
        }
    }
@Test
    public void testWaitWithLoggingError() throws Throwable {
        ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1);
        CompletableFuture<Integer> future = new CompletableFuture<>();
        executorService.schedule(() -> {
            future.completeExceptionally(new IllegalArgumentException("uh oh"));
        }, 1, TimeUnit.NANOSECONDS);
        assertEquals("Received a fatal error while waiting for the future to be completed",
            assertThrows(RuntimeException.class, () -> {
                FutureUtils.waitWithLogging(log,
                    "[FutureUtilsTest] ",
                    "the future to be completed",
                    future,
                    Deadline.fromDelay(Time.SYSTEM, 30, TimeUnit.SECONDS),
                    Time.SYSTEM);
            }).getMessage());
        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.MINUTES);
    }