< 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 "<logger>.handlers.ensureCloseOnReset". This defines a
+ * a boolean value. If "<logger>.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 "<logger>.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 "<logger>.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 >