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

Print this page

        

*** 142,152 **** * @since 1.4 */ public class LogManager { // The global LogManager object ! private static 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 --- 142,152 ---- * @since 1.4 */ public class LogManager { // The global LogManager object ! 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,165 **** 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; ! // 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; // Have we initialized global (root) handlers yet? --- 154,167 ---- 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(); ! // non final field - make it volatile to make sure that other threads ! // will see the new value once ensureLogManagerInitialized() has finished ! // executing. ! private volatile Logger rootLogger; // 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; // Have we initialized global (root) handlers yet?
*** 167,226 **** 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() { 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(); } catch (ClassNotFoundException ex) { ! Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname); ! manager = (LogManager) clz.newInstance(); } } } catch (Exception ex) { System.err.println("Could not load Logmanager \"" + cname + "\""); ex.printStackTrace(); } ! if (manager == null) { ! manager = new LogManager(); } - // 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; } }); } --- 169,205 ---- private boolean initializedGlobalHandlers = true; // True if JVM death is imminent and the exit hook has been called. private boolean deathImminent; static { ! 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); ! mgr = (LogManager) clz.newInstance(); } catch (ClassNotFoundException ex) { ! 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 (mgr == null) { ! mgr = new LogManager(); } + return mgr; } }); }
*** 233,242 **** --- 212,222 ---- * 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,284 **** // We do not need to register shutdownHook. } } /** * Returns the global LogManager object. * @return the global LogManager object */ public static LogManager getLogManager() { if (manager != null) { ! manager.readPrimordialConfiguration(); } return manager; } private void readPrimordialConfiguration() { --- 249,355 ---- // 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) { + // If initializedCalled is true it means that we're already in + // the process of initializing the LogManager in this thread. + // There has been a recursive call to ensureLogManagerInitialized(). + final boolean isRecursiveInitialization = (initializedCalled == true); + + assert initializedCalled || !initializationDone + : "Initialization can't be done if initialized has not been called!"; + + if (isRecursiveInitialization || initializationDone) { + // If isRecursiveInitialization is true it means that we're + // already in the process of initializing the LogManager in + // this thread. There has been a recursive call to + // ensureLogManagerInitialized(). 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. + 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.ensureLogManagerInitialized(); } return manager; } private void readPrimordialConfiguration() {
*** 293,313 **** } readPrimordialConfiguration = true; try { AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { 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(); } } } } } --- 364,384 ---- } 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) { ! assert false : "Exception raised while reading logging configuration: " + ex; } } } } }
*** 390,400 **** } } } // LoggerContext maps from AppContext ! private static 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; --- 461,471 ---- } } } // LoggerContext maps from AppContext ! 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,437 **** 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); contextsMap.put(ecx, context); } } } } // for standalone app, return userContext return context != null ? context : userContext; } private List<LoggerContext> contexts() { List<LoggerContext> cxs = new ArrayList<>(); ! cxs.add(systemContext); cxs.add(getUserContext()); return cxs; } // Find or create a specified logger instance. If a logger has --- 483,510 ---- contextsMap = new WeakHashMap<>(); } context = contextsMap.get(ecx); if (context == null) { // Create a new LoggerContext for the applet. ! 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(getSystemContext()); cxs.add(getUserContext()); return cxs; } // Find or create a specified logger instance. If a logger has
*** 448,458 **** // 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); do { if (addLogger(newLogger)) { // We successfully added the new Logger that we // created above so return it without refetching. return newLogger; --- 521,531 ---- // 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, this); do { if (addLogger(newLogger)) { // We successfully added the new Logger that we // created above so return it without refetching. return newLogger;
*** 475,485 **** 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); // 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. --- 548,558 ---- return result; } Logger demandSystemLogger(String name, String resourceBundleName) { // Add a system logger in the system context's namespace ! 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,508 **** --- 572,582 ---- // 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,544 **** // 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 { // 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; } 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); } // Due to subtle deadlock issues getUserContext() no longer // calls addLocalLogger(rootLogger); --- 591,646 ---- // 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. ! 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 LoggerContext() { this.root = new LogNode(null, this); ! } ! ! ! // 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. ! final LogManager owner = getOwner(); ! return owner.demandLogger(name, resourceBundleName, null); } // Due to subtle deadlock issues getUserContext() no longer // calls addLocalLogger(rootLogger);
*** 546,559 **** // Checks that the context is properly initialized // This is necessary before calling e.g. find(name) // or getLoggerNames() // private void ensureInitialized() { ! if (requiresDefaultLoggers) { // Ensure that the root and global loggers are set. ! ensureDefaultLogger(manager.rootLogger); ! ensureDefaultLogger(Logger.global); } } synchronized Logger findLogger(String name) { --- 648,661 ---- // Checks that the context is properly initialized // This is necessary before calling e.g. find(name) // or getLoggerNames() // private void ensureInitialized() { ! if (requiresDefaultLoggers()) { // Ensure that the root and global loggers are set. ! ensureDefaultLogger(getRootLogger()); ! ensureDefaultLogger(getGlobalLogger()); } } synchronized Logger findLogger(String name) {
*** 578,594 **** // '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) { final String name = logger.getName(); if (!name.isEmpty()) { ! ensureDefaultLogger(manager.rootLogger); ! } if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) { ! ensureDefaultLogger(Logger.global); } } } private void ensureDefaultLogger(Logger logger) { --- 680,696 ---- // '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()) { final String name = logger.getName(); if (!name.isEmpty()) { ! ensureDefaultLogger(getRootLogger()); if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) { ! ensureDefaultLogger(getGlobalLogger()); ! } } } } private void ensureDefaultLogger(Logger logger) {
*** 596,607 **** // 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) { // 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 --- 698,709 ---- // 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 != 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,633 **** } } boolean addLocalLogger(Logger logger) { // no need to add default loggers if it's not required ! 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) { --- 725,735 ---- } } boolean addLocalLogger(Logger logger) { // no need to add default loggers if it's not required ! 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,675 **** } } // We're adding a new logger. // Note that we are creating a weak reference here. ! ref = manager.new LoggerWeakRef(logger); namedLoggers.put(name, ref); // Apply any initial level defined for the new logger. ! Level level = manager.getLevelProperty(name + ".level", null); if (level != null) { doSetLevel(logger, level); } // instantiation of the handler is done in the LogManager.addLogger --- 763,779 ---- } } // We're adding a new logger. // Note that we are creating a weak reference here. ! 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 = owner.getLevelProperty(name + ".level", null); if (level != null) { doSetLevel(logger, level); } // instantiation of the handler is done in the LogManager.addLogger
*** 717,730 **** } // 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) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { ! if (logger != manager.rootLogger) { ! boolean useParent = manager.getBooleanProperty(name + ".useParentHandlers", true); if (!useParent) { logger.setUseParentHandlers(false); } } return null; --- 821,836 ---- } // 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 != owner.rootLogger) { ! boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true); if (!useParent) { logger.setUseParentHandlers(false); } } return null;
*** 736,747 **** 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) { // This pname has a level/handlers definition. // Make sure it exists. demandLogger(pname, null); } ix = ix2+1; --- 842,853 ---- int ix2 = name.indexOf(".", ix); if (ix2 < 0) { break; } String pname = name.substring(0, ix2); ! 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,796 **** } return node; } } ! static 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. 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); do { if (addLocalLogger(newLogger)) { // We successfully added the new Logger that we // created above so return it without refetching. result = newLogger; --- 883,903 ---- } return node; } } ! 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, null, getOwner()); do { if (addLocalLogger(newLogger)) { // We successfully added the new Logger that we // created above so return it without refetching. result = newLogger;
*** 820,829 **** --- 927,937 ---- // 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,1021 **** --- 1120,1130 ---- 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,1039 **** --- 1139,1149 ---- 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,1144 **** } 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 { readConfiguration(bin); - } finally { - if (in != null) { - in.close(); - } } } /** * Reset the logging configuration. --- 1237,1249 ---- } File f = new File(fname, "lib"); f = new File(f, "logging.properties"); fname = f.getCanonicalPath(); } ! try (final InputStream in = new FileInputStream(fname)) { ! final BufferedInputStream bin = new BufferedInputStream(in); readConfiguration(bin); } } /** * Reset the logging configuration.
*** 1199,1209 **** if (hands == null) { return new String[0]; } hands = hands.trim(); int ix = 0; ! Vector<String> result = new Vector<>(); while (ix < hands.length()) { int end = ix; while (end < hands.length()) { if (Character.isWhitespace(hands.charAt(end))) { break; --- 1304,1314 ---- if (hands == null) { return new String[0]; } hands = hands.trim(); int ix = 0; ! 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,1500 **** } // 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 RootLogger() { ! super("", null); setLevel(defaultLevel); } public void log(LogRecord record) { // Make sure that the global handlers have been instantiated. initializeGlobalHandlers(); super.log(record); } public void addHandler(Handler h) { initializeGlobalHandlers(); super.addHandler(h); } public void removeHandler(Handler h) { initializeGlobalHandlers(); super.removeHandler(h); } public Handler[] getHandlers() { initializeGlobalHandlers(); return super.getHandlers(); } } --- 1574,1612 ---- } // We use a subclass of Logger for the root logger, so // that we only instantiate the global handlers when they // are first needed. ! private final class RootLogger extends Logger { private RootLogger() { ! // 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(); } }