Skip to content

Commit

Permalink
concurrent-api: defer logging during CapturedContextProviders initial…
Browse files Browse the repository at this point in the history
…ization

Motivation:

The MDC utilities depend on the AsyncContext for storing properties.
This can create a cyclical dependency since we try to log the
loaded providers as part of the initialization.

Modifications:

Submit the logging to a different thread so that it will happen 'later'
after the class initialization is complete.
  • Loading branch information
bryce-anderson committed Mar 1, 2025
1 parent 57fac32 commit 467d834
Showing 1 changed file with 14 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,28 @@ static List<CapturedContextProvider> providers() {
return PROVIDERS;
}

// TODO: this was copied from `ServiceLoaderUtils` because the `CapturedContextProvider` interface is package
// private and the call to `ServiceLoader.load(..)` can only load implementations for types that are visible
// to the package from which it is called (because it checks if it's accessible by checking stack trace).
// One we make CapturedContextProvider public we can return to using ServiceLoaderUtils.
// This was copied from `ServiceLoaderUtils` because the implementation there logs the result, which normally
// wouldn't be a problem but as it turns out things like the MDC logging utils depend on the AsyncContext which
// may result in a cyclical dependency in class initialization.
private static <T> List<T> loadProviders(final Class<T> clazz, final ClassLoader classLoader, final Logger logger) {
final List<T> list = new ArrayList<>(0);
for (T provider : ServiceLoader.load(clazz, classLoader)) {
list.add(provider);
}
if (list.isEmpty()) {
logger.debug("ServiceLoader {}(s) registered: []", clazz.getSimpleName());
runLater(() -> logger.debug("ServiceLoader {}(s) registered: []", clazz.getSimpleName()));
return emptyList();
}
logger.info("ServiceLoader {}(s) registered: {}", clazz.getSimpleName(), list);
runLater(() -> logger.info("ServiceLoader {}(s) registered: {}", clazz.getSimpleName(), list));
return unmodifiableList(list);
}

// We have to run logging code 'later' because of cyclical dependency issues that can arise if logging depends
// on the AsyncContext.
private static void runLater(Runnable runnable) {
Thread t = new Thread(runnable);
t.setDaemon(true);
t.setName(CapturedContextProviders.class.getSimpleName() + "-logging");
t.start();
}
}

0 comments on commit 467d834

Please sign in to comment.