/** * Executes (and returns the result) of the given callable, but surrounds that execution with a try/catch that * should allow SLF4J logging replay to occur in cases where the log messages would otherwise get swallowed. One * example where this is necessary is when launching an application while using a java agent (e.g. the NewRelic * agent) - if the app crashes before enough time has passed, some or all of the log messages sent to SLF4J would * not get a chance to be output, swallowing potentially crucial info about what went wrong. By catching any * exception and waiting long enough before letting the exception propagate, we give the agent enough time to let * SLF4J replay any log messages that were stored. The app will still crash exactly as it normally would, but the * application log should be filled with all log messages sent to SLF4J before it crashes. * * <p>See http://www.slf4j.org/codes.html#replay for more info. * * <p>This method will use the {@link #DEFAULT_CRASH_DELAY_MILLIS} as the crash delay value. If you want to specify * a different value you can call {@link #executeCallableWithLoggingReplayProtection(Callable, long)} instead. */ public static <T> T executeCallableWithLoggingReplayProtection(Callable<T> callable) throws Exception { return executeCallableWithLoggingReplayProtection(callable, DEFAULT_CRASH_DELAY_MILLIS); }
/** * Call this when you're ready to launch/start the server. The {@code args} argument should be the same as what's * passed into the application's {@code public static void main} method entrypoint. */ @SuppressWarnings("UnusedParameters") public void launchServer(String[] args) throws Exception { MainClassUtils.executeCallableWithLoggingReplayProtection(() -> { infrastructureInit(); startServer(); return null; }); }
/** * Call this when you're ready to launch/start the server. The {@code args} argument should be the same as what's * passed into the application's {@code public static void main} method entrypoint. */ @SuppressWarnings("UnusedParameters") public void launchServer(String[] args) throws Exception { MainClassUtils.executeCallableWithLoggingReplayProtection(() -> { infrastructureInit(); startServer(); return null; }); }
@Test public void executeCallableWithLoggingReplayProtection_does_nothing_if_callable_does_not_throw_exception() throws Exception { // given String expectedResult = UUID.randomUUID().toString(); Callable<String> callable = () -> expectedResult; // when String result = MainClassUtils.executeCallableWithLoggingReplayProtection(callable); // then assertThat(result).isEqualTo(expectedResult); }
@Test public void executeCallableWithLoggingReplayProtection_delays_by_specified_amount_if_callable_throws_exception() { // given System.setProperty(DELAY_CRASH_ON_STARTUP_SYSTEM_PROP_KEY, "true"); RuntimeException ex = new RuntimeException("kaboom"); Callable<String> callable = () -> { throw ex; }; long delay = 200; // when long beforeMillis = System.currentTimeMillis(); Throwable caughtEx = catchThrowable(() -> MainClassUtils.executeCallableWithLoggingReplayProtection(callable, delay)); long afterMillis = System.currentTimeMillis(); // then assertThat(caughtEx).isSameAs(ex); assertThat(afterMillis - beforeMillis).isGreaterThanOrEqualTo(delay); }
@DataProvider(value = { "true", "false" }) @Test public void executeCallableWithLoggingReplayProtection_does_not_delay_on_exception_if_system_property_is_set( boolean useNull ) { // given if (useNull) System.clearProperty(DELAY_CRASH_ON_STARTUP_SYSTEM_PROP_KEY); else System.setProperty(DELAY_CRASH_ON_STARTUP_SYSTEM_PROP_KEY, "false"); RuntimeException ex = new RuntimeException("kaboom"); Callable<String> callable = () -> { throw ex; }; long delay = 200; // when long beforeMillis = System.currentTimeMillis(); Throwable caughtEx = catchThrowable(() -> MainClassUtils.executeCallableWithLoggingReplayProtection(callable, delay)); long afterMillis = System.currentTimeMillis(); // then assertThat(caughtEx).isSameAs(ex); assertThat(afterMillis - beforeMillis).isLessThan(delay); } }