src/share/classes/java/util/logging/LogManager.java
Print this page
@@ -142,11 +142,11 @@
* @since 1.4
*/
public class LogManager {
// The global LogManager object
- private static LogManager manager;
+ private static final LogManager manager;
private Properties props = new Properties();
private final static Level defaultLevel = Level.INFO;
// The map of the registered listeners. The map value is the registration
@@ -154,11 +154,13 @@
private final Map<Object,Integer> listenerMap = new HashMap<>();
// LoggerContext for system loggers and user loggers
private final LoggerContext systemContext = new SystemLoggerContext();
private final LoggerContext userContext = new LoggerContext();
- private Logger rootLogger;
+ private volatile Logger rootLogger; // non final field - make it volatile to
+ // make sure that other threads will see the new value once
+ // ensureLogManagerInitialized() has finished executing.
// Have we done the primordial reading of the configuration file?
// (Must be done after a suitable amount of java.lang.System
// initialization has been done)
private volatile boolean readPrimordialConfiguration;
@@ -167,60 +169,37 @@
private boolean initializedGlobalHandlers = true;
// True if JVM death is imminent and the exit hook has been called.
private boolean deathImminent;
static {
- AccessController.doPrivileged(new PrivilegedAction<Object>() {
- public Object run() {
+ manager = AccessController.doPrivileged(new PrivilegedAction<LogManager>() {
+ @Override
+ public LogManager run() {
+ LogManager mgr = null;
String cname = null;
try {
cname = System.getProperty("java.util.logging.manager");
if (cname != null) {
try {
- Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
- manager = (LogManager) clz.newInstance();
+ Class<?> clz = ClassLoader.getSystemClassLoader()
+ .loadClass(cname);
+ mgr = (LogManager) clz.newInstance();
} catch (ClassNotFoundException ex) {
- Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
- manager = (LogManager) clz.newInstance();
+ Class<?> clz = Thread.currentThread()
+ .getContextClassLoader().loadClass(cname);
+ mgr = (LogManager) clz.newInstance();
}
}
} catch (Exception ex) {
System.err.println("Could not load Logmanager \"" + cname + "\"");
ex.printStackTrace();
}
- if (manager == null) {
- manager = new LogManager();
+ if (mgr == null) {
+ mgr = new LogManager();
}
+ return mgr;
- // Create and retain Logger for the root of the namespace.
- manager.rootLogger = manager.new RootLogger();
- // since by design the global manager's userContext and
- // systemContext don't have their requiresDefaultLoggers
- // flag set - we make sure to add the root logger to
- // the global manager's default contexts here.
- manager.addLogger(manager.rootLogger);
- manager.systemContext.addLocalLogger(manager.rootLogger, false);
- manager.userContext.addLocalLogger(manager.rootLogger, false);
-
- // Adding the global Logger. Doing so in the Logger.<clinit>
- // would deadlock with the LogManager.<clinit>.
- // Do not call Logger.getGlobal() here as this might trigger
- // the deadlock too.
- @SuppressWarnings("deprecation")
- final Logger global = Logger.global;
- global.setLogManager(manager);
-
- // Make sure the global logger will be registered in the
- // global manager's default contexts.
- manager.addLogger(global);
- manager.systemContext.addLocalLogger(global, false);
- manager.userContext.addLocalLogger(global, false);
-
- // We don't call readConfiguration() here, as we may be running
- // very early in the JVM startup sequence. Instead readConfiguration
- // will be called lazily in getLogManager().
- return null;
}
});
}
@@ -233,10 +212,11 @@
* keeping a strong reference to an application classloader.
*/
this.setContextClassLoader(null);
}
+ @Override
public void run() {
// This is to ensure the LogManager.<clinit> is completed
// before synchronized block. Otherwise deadlocks are possible.
LogManager mgr = manager;
@@ -269,16 +249,99 @@
// We do not need to register shutdownHook.
}
}
/**
+ * Lazy initialization: if this instance of manager is the global
+ * manager then this method will read the initial configuration and
+ * add the root logger and global logger by calling addLogger().
+ *
+ * Note that it is subtly different from what we do in LoggerContext.
+ * In LoggerContext we're patching up the logger context tree in order to add
+ * the root and global logger *to the context tree*.
+ *
+ * For this to work, addLogger() must have already have been called
+ * once on the LogManager instance for the default logger being
+ * added.
+ *
+ * This is why ensureLogManagerInitialized() needs to be called before
+ * any logger is added to any logger context.
+ *
+ **/
+ private boolean initializedCalled=false;
+ private volatile boolean initializationDone=false;
+ final void ensureLogManagerInitialized() {
+ final LogManager owner = this;
+ if (initializationDone || owner != manager) {
+ // we don't want to do this twice, and we don't want to do
+ // this on private manager instances.
+ return;
+ }
+
+ // Maybe another thread has called ensureLogManagerInitialized()
+ // before us and is still executing it. If so we will block until
+ // the log manager has finished initialized, then acquire the monitor,
+ // notice that initializationDone is now true and return.
+ // Otherwise - we have come here first! We will acquire the monitor,
+ // see that initializationDone is still false, and perform the
+ // initialization.
+ //
+ synchronized(this) {
+ assert initializedCalled || !initializationDone
+ : "Initialization can't be done if initialized has not been called!";
+ if (initializedCalled || initializationDone) {
+ // If initializationCalled is true and initializationDone is
+ // false it means that we're in the process of initializing
+ // the LogManager in this thread. We should not proceed as
+ // it would lead to infinite recursion.
+ // If initializationDone is true then it means the manager
+ // has finished initializing; just return: we're done.
+ return;
+ }
+ // Calling addLogger below will in turn call requiresDefaultLogger()
+ // which will call ensureLogManagerInitialized().
+ // We use initializedCalled to break the recursion.
+ initializedCalled = true;
+ try {
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ assert rootLogger == null;
+ assert initializedCalled && !initializationDone;
+ // Read configuration. This was previously triggered
+ // by the new RootLogger() constructor - but no longer.
+ owner.readPrimordialConfiguration();
+
+ // Create and retain Logger for the root of the namespace.
+ owner.rootLogger = owner.new RootLogger();
+ owner.addLogger(owner.rootLogger);
+
+ // Adding the global Logger.
+ // Do not call Logger.getGlobal() here as this might trigger
+ // subtle inter-dependency issues.
+ @SuppressWarnings("deprecation")
+ final Logger global = Logger.global;
+
+ // Make sure the global logger will be registered in the
+ // global manager
+ owner.addLogger(global);
+ return null;
+ }
+ });
+ } finally {
+ initializationDone = true;
+ }
+ }
+ }
+
+ /**
* Returns the global LogManager object.
* @return the global LogManager object
*/
public static LogManager getLogManager() {
if (manager != null) {
- manager.readPrimordialConfiguration();
+ manager.ensureLogManagerInitialized();
}
return manager;
}
private void readPrimordialConfiguration() {
@@ -293,21 +356,21 @@
}
readPrimordialConfiguration = true;
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
+ @Override
public Void run() throws Exception {
readConfiguration();
// Platform loggers begin to delegate to java.util.logging.Logger
sun.util.logging.PlatformLogger.redirectPlatformLoggers();
return null;
}
});
} catch (Exception ex) {
- // System.err.println("Can't read logging configuration:");
- // ex.printStackTrace();
+ assert false : "Exception raised while reading logging configuration: " + ex;
}
}
}
}
}
@@ -390,11 +453,11 @@
}
}
}
// LoggerContext maps from AppContext
- private static WeakHashMap<Object, LoggerContext> contextsMap = null;
+ private WeakHashMap<Object, LoggerContext> contextsMap = null;
// Returns the LoggerContext for the user code (i.e. application or AppContext).
// Loggers are isolated from each AppContext.
private LoggerContext getUserContext() {
LoggerContext context = null;
@@ -412,26 +475,28 @@
contextsMap = new WeakHashMap<>();
}
context = contextsMap.get(ecx);
if (context == null) {
// Create a new LoggerContext for the applet.
- // The new logger context has its requiresDefaultLoggers
- // flag set to true - so that these loggers will be
- // lazily added when the context is firt accessed.
- context = new LoggerContext(true);
+ context = new LoggerContext();
contextsMap.put(ecx, context);
}
}
}
}
// for standalone app, return userContext
return context != null ? context : userContext;
}
+ // The system context.
+ final LoggerContext getSystemContext() {
+ return systemContext;
+ }
+
private List<LoggerContext> contexts() {
List<LoggerContext> cxs = new ArrayList<>();
- cxs.add(systemContext);
+ cxs.add(getSystemContext());
cxs.add(getUserContext());
return cxs;
}
// Find or create a specified logger instance. If a logger has
@@ -448,11 +513,11 @@
// readConfiguration, and other methods.
Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
Logger result = getLogger(name);
if (result == null) {
// only allocate the new logger once
- Logger newLogger = new Logger(name, resourceBundleName, caller);
+ Logger newLogger = new Logger(name, resourceBundleName, caller, this);
do {
if (addLogger(newLogger)) {
// We successfully added the new Logger that we
// created above so return it without refetching.
return newLogger;
@@ -475,11 +540,11 @@
return result;
}
Logger demandSystemLogger(String name, String resourceBundleName) {
// Add a system logger in the system context's namespace
- final Logger sysLogger = systemContext.demandLogger(name, resourceBundleName);
+ final Logger sysLogger = getSystemContext().demandLogger(name, resourceBundleName);
// Add the system logger to the LogManager's namespace if not exist
// so that there is only one single logger of the given name.
// System loggers are visible to applications unless a logger of
// the same name has been added.
@@ -499,10 +564,11 @@
// LogManager will set the sysLogger's handlers via LogManager.addLogger method.
if (logger != sysLogger && sysLogger.getHandlers().length == 0) {
// if logger already exists but handlers not set
final Logger l = logger;
AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
public Void run() {
for (Handler hdl : l.getHandlers()) {
sysLogger.addHandler(hdl);
}
return null;
@@ -517,28 +583,56 @@
// context. The system context is used to maintain the namespace for
// all system loggers and is queried by the system code. If a system logger
// doesn't exist in the user context, it'll also be added to the user context.
// The user context is queried by the user code and all other loggers are
// added in the user context.
- static class LoggerContext {
+ class LoggerContext {
// Table of named Loggers that maps names to Loggers.
private final Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();
// Tree of named Loggers
private final LogNode root;
- private final boolean requiresDefaultLoggers;
private LoggerContext() {
- this(false);
- }
- private LoggerContext(boolean requiresDefaultLoggers) {
this.root = new LogNode(null, this);
- this.requiresDefaultLoggers = requiresDefaultLoggers;
+ }
+
+
+ // Tells whether default loggers are required in this context.
+ // If true, the default loggers will be lazily added.
+ final boolean requiresDefaultLoggers() {
+ final boolean requiresDefaultLoggers = (getOwner() == manager);
+ if (requiresDefaultLoggers) {
+ getOwner().ensureLogManagerInitialized();
+ }
+ return requiresDefaultLoggers;
+ }
+
+ // This context's LogManager.
+ final LogManager getOwner() {
+ return LogManager.this;
+ }
+
+ // This context owner's root logger, which if not null, and if
+ // the context requires default loggers, will be added to the context
+ // logger's tree.
+ final Logger getRootLogger() {
+ return getOwner().rootLogger;
+ }
+
+ // The global logger, which if not null, and if
+ // the context requires default loggers, will be added to the context
+ // logger's tree.
+ final Logger getGlobalLogger() {
+ @SuppressWarnings("deprecated") // avoids initialization cycles.
+ final Logger global = Logger.global;
+ return global;
}
Logger demandLogger(String name, String resourceBundleName) {
// a LogManager subclass may have its own implementation to add and
// get a Logger. So delegate to the LogManager to do the work.
- return manager.demandLogger(name, resourceBundleName, null);
+ final LogManager owner = getOwner();
+ return owner.demandLogger(name, resourceBundleName, null);
}
// Due to subtle deadlock issues getUserContext() no longer
// calls addLocalLogger(rootLogger);
@@ -546,14 +640,14 @@
// Checks that the context is properly initialized
// This is necessary before calling e.g. find(name)
// or getLoggerNames()
//
private void ensureInitialized() {
- if (requiresDefaultLoggers) {
+ if (requiresDefaultLoggers()) {
// Ensure that the root and global loggers are set.
- ensureDefaultLogger(manager.rootLogger);
- ensureDefaultLogger(Logger.global);
+ ensureDefaultLogger(getRootLogger());
+ ensureDefaultLogger(getGlobalLogger());
}
}
synchronized Logger findLogger(String name) {
@@ -578,17 +672,17 @@
// 'logger' is the context that will be added.
// This method will ensure that the defaults loggers are added
// before adding 'logger'.
//
private void ensureAllDefaultLoggers(Logger logger) {
- if (requiresDefaultLoggers) {
+ if (requiresDefaultLoggers()) {
final String name = logger.getName();
if (!name.isEmpty()) {
- ensureDefaultLogger(manager.rootLogger);
- }
+ ensureDefaultLogger(getRootLogger());
if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) {
- ensureDefaultLogger(Logger.global);
+ ensureDefaultLogger(getGlobalLogger());
+ }
}
}
}
private void ensureDefaultLogger(Logger logger) {
@@ -596,12 +690,12 @@
// to a LoggerContext.
// This check is simple sanity: we do not want that this
// method be called for anything else than Logger.global
// or owner.rootLogger.
- if (!requiresDefaultLoggers || logger == null
- || logger != Logger.global && logger != manager.rootLogger) {
+ if (!requiresDefaultLoggers() || logger == null
+ || logger != Logger.global && logger != LogManager.this.rootLogger) {
// the case where we have a non null logger which is neither
// Logger.global nor manager.rootLogger indicates a serious
// issue - as ensureDefaultLogger should never be called
// with any other loggers than one of these two (or null - if
@@ -623,11 +717,11 @@
}
}
boolean addLocalLogger(Logger logger) {
// no need to add default loggers if it's not required
- return addLocalLogger(logger, requiresDefaultLoggers);
+ return addLocalLogger(logger, requiresDefaultLoggers());
}
// Add a logger to this context. This method will only set its level
// and process parent loggers. It doesn't set its handlers.
synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) {
@@ -661,15 +755,17 @@
}
}
// We're adding a new logger.
// Note that we are creating a weak reference here.
- ref = manager.new LoggerWeakRef(logger);
+ final LogManager owner = getOwner();
+ logger.setLogManager(owner);
+ ref = owner.new LoggerWeakRef(logger);
namedLoggers.put(name, ref);
// Apply any initial level defined for the new logger.
- Level level = manager.getLevelProperty(name + ".level", null);
+ Level level = owner.getLevelProperty(name + ".level", null);
if (level != null) {
doSetLevel(logger, level);
}
// instantiation of the handler is done in the LogManager.addLogger
@@ -717,14 +813,16 @@
}
// If logger.getUseParentHandlers() returns 'true' and any of the logger's
// parents have levels or handlers defined, make sure they are instantiated.
private void processParentHandlers(final Logger logger, final String name) {
+ final LogManager owner = getOwner();
AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
public Void run() {
- if (logger != manager.rootLogger) {
- boolean useParent = manager.getBooleanProperty(name + ".useParentHandlers", true);
+ if (logger != owner.rootLogger) {
+ boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true);
if (!useParent) {
logger.setUseParentHandlers(false);
}
}
return null;
@@ -736,12 +834,12 @@
int ix2 = name.indexOf(".", ix);
if (ix2 < 0) {
break;
}
String pname = name.substring(0, ix2);
- if (manager.getProperty(pname + ".level") != null ||
- manager.getProperty(pname + ".handlers") != null) {
+ if (owner.getProperty(pname + ".level") != null ||
+ owner.getProperty(pname + ".handlers") != null) {
// This pname has a level/handlers definition.
// Make sure it exists.
demandLogger(pname, null);
}
ix = ix2+1;
@@ -777,20 +875,21 @@
}
return node;
}
}
- static class SystemLoggerContext extends LoggerContext {
+ final class SystemLoggerContext extends LoggerContext {
// Add a system logger in the system context's namespace as well as
// in the LogManager's namespace if not exist so that there is only
// one single logger of the given name. System loggers are visible
// to applications unless a logger of the same name has been added.
+ @Override
Logger demandLogger(String name, String resourceBundleName) {
Logger result = findLogger(name);
if (result == null) {
// only allocate the new system logger once
- Logger newLogger = new Logger(name, resourceBundleName);
+ Logger newLogger = new Logger(name, resourceBundleName, null, getOwner());
do {
if (addLocalLogger(newLogger)) {
// We successfully added the new Logger that we
// created above so return it without refetching.
result = newLogger;
@@ -820,10 +919,11 @@
// only be modified by trusted code.
private void loadLoggerHandlers(final Logger logger, final String name,
final String handlersPropertyName)
{
AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
public Object run() {
String names[] = parseClassNames(handlersPropertyName);
for (int i = 0; i < names.length; i++) {
String word = names[i];
try {
@@ -1012,10 +1112,11 @@
return;
}
// There is a security manager. Raise privilege before
// calling setLevel.
AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
public Object run() {
logger.setLevel(level);
return null;
}});
}
@@ -1030,10 +1131,11 @@
return;
}
// There is a security manager. Raise privilege before
// calling setParent.
AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
public Object run() {
logger.setParent(parent);
return null;
}});
}
@@ -1127,18 +1229,13 @@
}
File f = new File(fname, "lib");
f = new File(f, "logging.properties");
fname = f.getCanonicalPath();
}
- InputStream in = new FileInputStream(fname);
- BufferedInputStream bin = new BufferedInputStream(in);
- try {
+ try (final InputStream in = new FileInputStream(fname)) {
+ final BufferedInputStream bin = new BufferedInputStream(in);
readConfiguration(bin);
- } finally {
- if (in != null) {
- in.close();
- }
}
}
/**
* Reset the logging configuration.
@@ -1199,11 +1296,11 @@
if (hands == null) {
return new String[0];
}
hands = hands.trim();
int ix = 0;
- Vector<String> result = new Vector<>();
+ final List<String> result = new ArrayList<>();
while (ix < hands.length()) {
int end = ix;
while (end < hands.length()) {
if (Character.isWhitespace(hands.charAt(end))) {
break;
@@ -1469,32 +1566,39 @@
}
// We use a subclass of Logger for the root logger, so
// that we only instantiate the global handlers when they
// are first needed.
- private class RootLogger extends Logger {
+ private final class RootLogger extends Logger {
private RootLogger() {
- super("", null);
+ // We do not call the protected Logger two args constructor here,
+ // to avoid calling LogManager.getLogManager() from within the
+ // RootLogger constructor.
+ super("", null, null, LogManager.this);
setLevel(defaultLevel);
}
+ @Override
public void log(LogRecord record) {
// Make sure that the global handlers have been instantiated.
initializeGlobalHandlers();
super.log(record);
}
+ @Override
public void addHandler(Handler h) {
initializeGlobalHandlers();
super.addHandler(h);
}
+ @Override
public void removeHandler(Handler h) {
initializeGlobalHandlers();
super.removeHandler(h);
}
+ @Override
public Handler[] getHandlers() {
initializeGlobalHandlers();
return super.getHandlers();
}
}