This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 10.1.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 00390ed5d43e3cc423e129e97dea200715d49270 Author: opencode <[email protected]> AuthorDate: Tue Oct 20 10:26:37 2026 +0200 Release the log handlers of the given class loader in JULI LogFactory.release() never used its class loader argument. Since the JULI log manager resolves the configuration to reset through the context class loader of the current thread, the handlers of the stopping application were only closed when the thread happened to run with that class loader as the context one, and the handlers of whatever other class loader was current closed instead, leaking the open log files of the application until the VM exited. The release path of the container binds its thread to the class loader to stop beforehand, so there the argument and the context class loader already agree and the reset is a plain reset as before. In the other cases, the class loader to release is now made the context class loader for the duration of the reset and the previous one restored afterwards. The argument is documented as required and as briefly creating a missing configuration to release it, matching what the reset does internally. --- java/org/apache/juli/logging/LogFactory.java | 34 +++++++++++++++++++++------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/java/org/apache/juli/logging/LogFactory.java b/java/org/apache/juli/logging/LogFactory.java index 3b6bc5e2b0..285b5672f0 100644 --- a/java/org/apache/juli/logging/LogFactory.java +++ b/java/org/apache/juli/logging/LogFactory.java @@ -197,18 +197,36 @@ public class LogFactory { /** - * Reset the {@link java.util.logging.LogManager}, unless the standard <code>java.util.logging.LogManager</code> - * implementation is in use. The given class loader is not used: the JULI log manager associates its configuration - * with the current thread's context class loader. + * Release the log handlers that the JULI {@link java.util.logging.LogManager} has associated with the given class + * loader, so a stopped application no longer keeps its log files open. If the class loader has no logging + * configuration yet, one is briefly created for it and released again. This is a NO-OP when the standard + * <code>java.util.logging.LogManager</code> implementation is in use: that one has a single global configuration + * and resetting it would discard the logging of the whole JVM. The JULI log manager associates its configuration + * with the current thread's context class loader, so the given class loader is made current for the duration of + * the reset. * - * @param classLoader ClassLoader for which to release the LogFactory. Not used by this implementation. + * @param classLoader ClassLoader for which to release the LogFactory. Must not be null. */ public static void release(ClassLoader classLoader) { - // JULI's log manager looks at the current classLoader so there is no - // need to use the passed in classLoader, the default implementation - // does not so call reset in that case will break things + // JULI's log manager looks at the current classLoader so the + // classLoader to release has to be the current one, the default + // implementation does not so calling reset in that case would reset + // the global configuration and break the logging of the whole JVM if (!LogManager.getLogManager().getClass().getName().equals("java.util.logging.LogManager")) { - LogManager.getLogManager().reset(); + Thread currentThread = Thread.currentThread(); + ClassLoader originalClassLoader = currentThread.getContextClassLoader(); + if (originalClassLoader == classLoader) { + // The class loader to release is already the current one, as on the + // regular container stop path that binds its thread beforehand + LogManager.getLogManager().reset(); + } else { + try { + currentThread.setContextClassLoader(classLoader); + LogManager.getLogManager().reset(); + } finally { + currentThread.setContextClassLoader(originalClassLoader); + } + } } } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
