< prev index next >

src/java.logging/share/classes/java/util/logging/LogManager.java

Print this page

        

*** 29,38 **** --- 29,39 ---- import java.io.*; import java.util.*; import java.security.*; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; + import java.util.concurrent.CopyOnWriteArrayList; import sun.misc.JavaAWTAccess; import sun.misc.SharedSecrets; /** * There is a single global LogManager object that is used to
*** 98,107 **** --- 99,121 ---- * load and register as handlers to the specified logger. Each class * name must be for a Handler class which has a default constructor. * Note that these Handlers may be created lazily, when they are * first used. * + * <li>A property "&lt;logger&gt;.handlers.ensureCloseOnReset". This defines a + * a boolean value. If "&lt;logger&gt;.handlers" is not defined or is empty, + * this property is ignored. Otherwise it defaults to {@code true}. When the + * value is {@code true}, the handlers associated with the logger are guaranteed + * to be closed on {@linkplain reset} and shutdown. This can be turned off + * by explicitly setting "&lt;logger&gt;.handlers.ensureCloseOnReset=false" in + * the configuration. Note that turning this property off causes the risk of + * introducing a resource leak, as the logger may get garbage collected before + * {@code reset()} is called, thus preventing its handlers from being closed + * on {@code reset()}. In that case it is the responsibility of the application + * to ensure that the handlers are closed before the logger is garbage + * collected. + * * <li>A property "&lt;logger&gt;.useParentHandlers". This defines a boolean * value. By default every logger calls its parent in addition to * handling the logging message itself, this often result in messages * being handled by the root logger as well. When setting this property * to false a Handler needs to be configured for this logger otherwise
*** 167,176 **** --- 181,217 ---- // This gets set to false in readConfiguration private boolean initializedGlobalHandlers = true; // True if JVM death is imminent and the exit hook has been called. private boolean deathImminent; + // This list contains the loggers for which some handlers have been + // explicitly configured in the configuration file. + // It prevents these loggers from being arbitrarily garbage collected. + private static final class CloseOnReset { + private final Logger logger; + private CloseOnReset(Logger ref) { + this.logger = Objects.requireNonNull(ref); + } + @Override + public boolean equals(Object other) { + return (other instanceof CloseOnReset) && ((CloseOnReset)other).logger == logger; + } + @Override + public int hashCode() { + return System.identityHashCode(logger); + } + public Logger get() { + return logger; + } + public static CloseOnReset create(Logger logger) { + return new CloseOnReset(logger); + } + } + private final CopyOnWriteArrayList<CloseOnReset> closeOnResetLoggers = + new CopyOnWriteArrayList<>(); + + private final Map<Object, Runnable> listeners = Collections.synchronizedMap(new IdentityHashMap<>()); static { manager = AccessController.doPrivileged(new PrivilegedAction<LogManager>() {
*** 202,212 **** } }); } - // This private class is used as a shutdown hook. // It does a "reset" to close all open handlers. private class Cleaner extends Thread { private Cleaner() { --- 243,252 ----
*** 873,906 **** { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { String names[] = parseClassNames(handlersPropertyName); ! for (String word : names) { try { ! Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word); Handler hdl = (Handler) clz.newInstance(); // Check if there is a property defining the // this handler's level. ! String levs = getProperty(word + ".level"); if (levs != null) { Level l = Level.findLevel(levs); if (l != null) { hdl.setLevel(l); } else { // Probably a bad level. Drop through. ! System.err.println("Can't set level for " + word); } } // Add this Handler to the logger logger.addHandler(hdl); } catch (Exception ex) { ! System.err.println("Can't load log handler \"" + word + "\""); System.err.println("" + ex); ex.printStackTrace(); } } return null; } }); } --- 913,956 ---- { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { String names[] = parseClassNames(handlersPropertyName); ! final boolean ensureCloseOnReset = names.length > 0 ! && getBooleanProperty( ! handlersPropertyName + ".ensureCloseOnReset", ! true); ! int count = 0; ! for (String type : names) { try { ! Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(type); Handler hdl = (Handler) clz.newInstance(); // Check if there is a property defining the // this handler's level. ! String levs = getProperty(type + ".level"); if (levs != null) { Level l = Level.findLevel(levs); if (l != null) { hdl.setLevel(l); } else { // Probably a bad level. Drop through. ! System.err.println("Can't set level for " + type); } } // Add this Handler to the logger logger.addHandler(hdl); + if (++count == 1 && ensureCloseOnReset) { + // add this logger to the closeOnResetLoggers list. + closeOnResetLoggers.addIfAbsent(CloseOnReset.create(logger)); + } } catch (Exception ex) { ! System.err.println("Can't load log handler \"" + type + "\""); System.err.println("" + ex); ex.printStackTrace(); } } + return null; } }); }
*** 1231,1242 **** --- 1281,1299 ---- * the caller does not have LoggingPermission("control"). */ public void reset() throws SecurityException { checkPermission(); + List<CloseOnReset> persistent; synchronized (this) { props = new Properties(); + // make sure we keep the loggers persistent until reset is done. + // Those are the loggers for which we previously created a + // handler from the configuration, and we need to prevent them + // from being gc'ed until those handlers are closed. + persistent = new ArrayList<>(closeOnResetLoggers); + closeOnResetLoggers.clear(); // Since we are doing a reset we no longer want to initialize // the global handlers, if they haven't been initialized yet. initializedGlobalHandlers = true; } for (LoggerContext cx : contexts()) {
*** 1247,1256 **** --- 1304,1314 ---- if (logger != null) { resetLogger(logger); } } } + persistent.clear(); } // Private method to reset an individual target logger. private void resetLogger(Logger logger) { // Close all the Logger's handlers.
< prev index next >