LogManager.java revision 10967:e336cbd8b15e
12038SN/A/* 217248Siignatyev * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. 32038SN/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 42038SN/A * 52038SN/A * This code is free software; you can redistribute it and/or modify it 62038SN/A * under the terms of the GNU General Public License version 2 only, as 78729Sserb * published by the Free Software Foundation. Oracle designates this 82038SN/A * particular file as subject to the "Classpath" exception as provided 92038SN/A * by Oracle in the LICENSE file that accompanied this code. 102038SN/A * 112038SN/A * This code is distributed in the hope that it will be useful, but WITHOUT 122038SN/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 132038SN/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 142038SN/A * version 2 for more details (a copy is included in the LICENSE file that 152038SN/A * accompanied this code). 162038SN/A * 172038SN/A * You should have received a copy of the GNU General Public License version 182038SN/A * 2 along with this work; if not, write to the Free Software Foundation, 192362SN/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 202362SN/A * 212362SN/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 222038SN/A * or visit www.oracle.com if you need additional information or have any 232038SN/A * questions. 242038SN/A */ 253793Sjrose 2617248Siignatyev 275688Stwistipackage java.util.logging; 2817248Siignatyev 2917248Siignatyevimport java.io.*; 3017248Siignatyevimport java.util.*; 3117248Siignatyevimport java.security.*; 322038SN/Aimport java.lang.ref.ReferenceQueue; 332038SN/Aimport java.lang.ref.WeakReference; 343793Sjroseimport sun.misc.JavaAWTAccess; 352038SN/Aimport sun.misc.SharedSecrets; 3617248Siignatyev 3717248Siignatyev/** 385688Stwisti * There is a single global LogManager object that is used to 3917248Siignatyev * maintain a set of shared state about Loggers and log services. 4017248Siignatyev * <p> 4117248Siignatyev * This LogManager object: 4217248Siignatyev * <ul> 4317248Siignatyev * <li> Manages a hierarchical namespace of Logger objects. All 443793Sjrose * named Loggers are stored in this namespace. 4517248Siignatyev * <li> Manages a set of logging control properties. These are 4617248Siignatyev * simple key-value pairs that can be used by Handlers and 4717248Siignatyev * other logging objects to configure themselves. 4817248Siignatyev * </ul> 4917248Siignatyev * <p> 5017248Siignatyev * The global LogManager object can be retrieved using LogManager.getLogManager(). 5117248Siignatyev * The LogManager object is created during class initialization and 5217248Siignatyev * cannot subsequently be changed. 5317248Siignatyev * <p> 5417248Siignatyev * At startup the LogManager class is located using the 5517248Siignatyev * java.util.logging.manager system property. 5617248Siignatyev * <p> 5717248Siignatyev * The LogManager defines two optional system properties that allow control over 5817248Siignatyev * the initial configuration: 5917248Siignatyev * <ul> 6017248Siignatyev * <li>"java.util.logging.config.class" 6117248Siignatyev * <li>"java.util.logging.config.file" 6217248Siignatyev * </ul> 6317248Siignatyev * These two properties may be specified on the command line to the "java" 6417248Siignatyev * command, or as system property definitions passed to JNI_CreateJavaVM. 652038SN/A * <p> 662038SN/A * If the "java.util.logging.config.class" property is set, then the 672038SN/A * property value is treated as a class name. The given class will be 682038SN/A * loaded, an object will be instantiated, and that object's constructor 692038SN/A * is responsible for reading in the initial configuration. (That object 702038SN/A * may use other system properties to control its configuration.) The 712038SN/A * alternate configuration class can use <tt>readConfiguration(InputStream)</tt> 724383Sjrose * to define properties in the LogManager. 732038SN/A * <p> 742431SN/A * If "java.util.logging.config.class" property is <b>not</b> set, 752435SN/A * then the "java.util.logging.config.file" system property can be used 764383Sjrose * to specify a properties file (in java.util.Properties format). The 774383Sjrose * initial logging configuration will be read from this file. 784383Sjrose * <p> 792435SN/A * If neither of these properties is defined then the LogManager uses its 802435SN/A * default configuration. The default configuration is typically loaded from the 812038SN/A * properties file "{@code conf/logging.properties}" in the Java installation 822038SN/A * directory. 832038SN/A * <p> 842038SN/A * The properties for loggers and Handlers will have names starting 855688Stwisti * with the dot-separated name for the handler or logger. 865688Stwisti * <p> 875688Stwisti * The global logging properties may include: 885688Stwisti * <ul> 895688Stwisti * <li>A property "handlers". This defines a whitespace or comma separated 905688Stwisti * list of class names for handler classes to load and register as 915688Stwisti * handlers on the root Logger (the Logger named ""). Each class 922038SN/A * name must be for a Handler class which has a default constructor. 934383Sjrose * Note that these Handlers may be created lazily, when they are 944383Sjrose * first used. 954383Sjrose * 962038SN/A * <li>A property "<logger>.handlers". This defines a whitespace or 972038SN/A * comma separated list of class names for handlers classes to 982038SN/A * load and register as handlers to the specified logger. Each class 992038SN/A * name must be for a Handler class which has a default constructor. 1002038SN/A * Note that these Handlers may be created lazily, when they are 1012431SN/A * first used. 1022038SN/A * 1032038SN/A * <li>A property "<logger>.useParentHandlers". This defines a boolean 1042038SN/A * value. By default every logger calls its parent in addition to 1052038SN/A * handling the logging message itself, this often result in messages 1062038SN/A * being handled by the root logger as well. When setting this property 1072038SN/A * to false a Handler needs to be configured for this logger otherwise 1082038SN/A * no logging messages are delivered. 1092038SN/A * 1102435SN/A * <li>A property "config". This property is intended to allow 1112038SN/A * arbitrary configuration code to be run. The property defines a 1122038SN/A * whitespace or comma separated list of class names. A new instance will be 1132038SN/A * created for each named class. The default constructor of each class 1142431SN/A * may execute arbitrary code to update the logging configuration, such as 1152038SN/A * setting logger levels, adding handlers, adding filters, etc. 1162038SN/A * </ul> 1172038SN/A * <p> 1182435SN/A * Note that all classes loaded during LogManager configuration are 1192435SN/A * first searched on the system class path before any user class path. 1202431SN/A * That includes the LogManager class, any config classes, and any 1212038SN/A * handler classes. 1222038SN/A * <p> 1232038SN/A * Loggers are organized into a naming hierarchy based on their 1242038SN/A * dot separated names. Thus "a.b.c" is a child of "a.b", but 1252038SN/A * "a.b1" and a.b2" are peers. 1262038SN/A * <p> 1272038SN/A * All properties whose names end with ".level" are assumed to define 1282038SN/A * log levels for Loggers. Thus "foo.level" defines a log level for 1292038SN/A * the logger called "foo" and (recursively) for any of its children 1302431SN/A * in the naming hierarchy. Log Levels are applied in the order they 1312038SN/A * are defined in the properties file. Thus level settings for child 1322038SN/A * nodes in the tree should come after settings for their parents. 1332038SN/A * The property name ".level" can be used to set the level for the 1342038SN/A * root of the tree. 1352038SN/A * <p> 1362038SN/A * All methods on the LogManager object are multi-thread safe. 1372038SN/A * 1382038SN/A * @since 1.4 1392038SN/A*/ 1402431SN/A 1412038SN/Apublic class LogManager { 1422038SN/A // The global LogManager object 1432038SN/A private static final LogManager manager; 1442038SN/A 1452435SN/A // 'props' is assigned within a lock but accessed without it. 1464383Sjrose // Declaring it volatile makes sure that another thread will not 1472435SN/A // be able to see a partially constructed 'props' object. 1482435SN/A // (seeing a partially constructed 'props' object can result in 1492435SN/A // NPE being thrown in Hashtable.get(), because it leaves the door 1502435SN/A // open for props.getProperties() to be called before the construcor 1512038SN/A // of Hashtable is actually completed). 1522038SN/A private volatile Properties props = new Properties(); 1534935Sjrose private final static Level defaultLevel = Level.INFO; 1542038SN/A 1552038SN/A // LoggerContext for system loggers and user loggers 1562038SN/A private final LoggerContext systemContext = new SystemLoggerContext(); 1575688Stwisti private final LoggerContext userContext = new LoggerContext(); 1582038SN/A // non final field - make it volatile to make sure that other threads 1592038SN/A // will see the new value once ensureLogManagerInitialized() has finished 1602038SN/A // executing. 1612038SN/A private volatile Logger rootLogger; 1622038SN/A // Have we done the primordial reading of the configuration file? 1632038SN/A // (Must be done after a suitable amount of java.lang.System 1642038SN/A // initialization has been done) 1652431SN/A private volatile boolean readPrimordialConfiguration; 1662038SN/A // Have we initialized global (root) handlers yet? 1678311Sjrose // This gets set to false in readConfiguration 1682038SN/A private boolean initializedGlobalHandlers = true; 1692038SN/A // True if JVM death is imminent and the exit hook has been called. 1702038SN/A private boolean deathImminent; 1712038SN/A 1722038SN/A private final Map<Object, Runnable> listeners = 1732038SN/A Collections.synchronizedMap(new IdentityHashMap<>()); 1742431SN/A 1758311Sjrose static { 1768311Sjrose manager = AccessController.doPrivileged(new PrivilegedAction<LogManager>() { 1778311Sjrose @Override 1788311Sjrose public LogManager run() { 1798311Sjrose LogManager mgr = null; 1808311Sjrose String cname = null; 1818311Sjrose try { 1828311Sjrose cname = System.getProperty("java.util.logging.manager"); 1838311Sjrose if (cname != null) { 1848311Sjrose try { 1858311Sjrose Class<?> clz = ClassLoader.getSystemClassLoader() 1868311Sjrose .loadClass(cname); 1878311Sjrose mgr = (LogManager) clz.newInstance(); 1888311Sjrose } catch (ClassNotFoundException ex) { 1898311Sjrose Class<?> clz = Thread.currentThread() 1908311Sjrose .getContextClassLoader().loadClass(cname); 1918311Sjrose mgr = (LogManager) clz.newInstance(); 1928311Sjrose } 1938311Sjrose } 1942038SN/A } catch (Exception ex) { 1952038SN/A System.err.println("Could not load Logmanager \"" + cname + "\""); 1962038SN/A ex.printStackTrace(); 1972038SN/A } 1982038SN/A if (mgr == null) { 1992038SN/A mgr = new LogManager(); 2002038SN/A } 2012038SN/A return mgr; 2022038SN/A 2032038SN/A } 2042038SN/A }); 2052038SN/A } 2064935Sjrose 2072038SN/A 2082038SN/A // This private class is used as a shutdown hook. 2092038SN/A // It does a "reset" to close all open handlers. 2102038SN/A private class Cleaner extends Thread { 2112038SN/A 2122038SN/A private Cleaner() { 2132038SN/A /* Set context class loader to null in order to avoid 2142038SN/A * keeping a strong reference to an application classloader. 2152038SN/A */ 2162038SN/A this.setContextClassLoader(null); 2172038SN/A } 2182038SN/A 2192038SN/A @Override 2202040SN/A public void run() { 2212040SN/A // This is to ensure the LogManager.<clinit> is completed 2222040SN/A // before synchronized block. Otherwise deadlocks are possible. 2232040SN/A LogManager mgr = manager; 2242038SN/A 2252038SN/A // If the global handlers haven't been initialized yet, we 2262038SN/A // don't want to initialize them just so we can close them! 2272431SN/A synchronized (LogManager.this) { 2282431SN/A // Note that death is imminent. 2292431SN/A deathImminent = true; 2302431SN/A initializedGlobalHandlers = true; 2312431SN/A } 2322431SN/A 2332431SN/A // Do a reset to close all active handlers. 2342431SN/A reset(); 2352431SN/A } 2362431SN/A } 2372431SN/A 2382431SN/A 2392431SN/A /** 2402431SN/A * Protected constructor. This is protected so that container applications 2412431SN/A * (such as J2EE containers) can subclass the object. It is non-public as 2422431SN/A * it is intended that there only be one LogManager object, whose value is 2432431SN/A * retrieved by calling LogManager.getLogManager. 2442431SN/A */ 2452431SN/A protected LogManager() { 2462431SN/A this(checkSubclassPermissions()); 2472431SN/A } 2482431SN/A 2492431SN/A private LogManager(Void checked) { 2502431SN/A 2512431SN/A // Add a shutdown hook to close the global handlers. 2522431SN/A try { 2532431SN/A Runtime.getRuntime().addShutdownHook(new Cleaner()); 2542431SN/A } catch (IllegalStateException e) { 2552431SN/A // If the VM is already shutting down, 2562431SN/A // We do not need to register shutdownHook. 2572431SN/A } 2582038SN/A } 2592431SN/A 2602038SN/A private static Void checkSubclassPermissions() { 2612038SN/A final SecurityManager sm = System.getSecurityManager(); 2622038SN/A if (sm != null) { 2633793Sjrose // These permission will be checked in the LogManager constructor, 2642038SN/A // in order to register the Cleaner() thread as a shutdown hook. 2652038SN/A // Check them here to avoid the penalty of constructing the object 2662038SN/A // etc... 2672038SN/A sm.checkPermission(new RuntimePermission("shutdownHooks")); 2682038SN/A sm.checkPermission(new RuntimePermission("setContextClassLoader")); 2693012SN/A } 2703012SN/A return null; 2713012SN/A } 2723012SN/A 2733012SN/A /** 2743012SN/A * Lazy initialization: if this instance of manager is the global 2758311Sjrose * manager then this method will read the initial configuration and 2768311Sjrose * add the root logger and global logger by calling addLogger(). 2778311Sjrose * 2788311Sjrose * Note that it is subtly different from what we do in LoggerContext. 2798311Sjrose * In LoggerContext we're patching up the logger context tree in order to add 2808311Sjrose * the root and global logger *to the context tree*. 2815685Sjrose * 2825685Sjrose * For this to work, addLogger() must have already have been called 2832038SN/A * once on the LogManager instance for the default logger being 2842431SN/A * added. 2852038SN/A * 2862038SN/A * This is why ensureLogManagerInitialized() needs to be called before 2872038SN/A * any logger is added to any logger context. 2884935Sjrose * 2892038SN/A */ 2902038SN/A private boolean initializedCalled = false; 2912038SN/A private volatile boolean initializationDone = false; 2922038SN/A final void ensureLogManagerInitialized() { 2932038SN/A final LogManager owner = this; 2942038SN/A if (initializationDone || owner != manager) { 2952038SN/A // we don't want to do this twice, and we don't want to do 2962038SN/A // this on private manager instances. 2972038SN/A return; 2982038SN/A } 2992038SN/A 3002038SN/A // Maybe another thread has called ensureLogManagerInitialized() 3012038SN/A // before us and is still executing it. If so we will block until 3022038SN/A // the log manager has finished initialized, then acquire the monitor, 3032038SN/A // notice that initializationDone is now true and return. 3048316Sjrose // Otherwise - we have come here first! We will acquire the monitor, 3058316Sjrose // see that initializationDone is still false, and perform the 3068316Sjrose // initialization. 3072038SN/A // 3084935Sjrose synchronized(this) { 3092038SN/A // If initializedCalled is true it means that we're already in 3102038SN/A // the process of initializing the LogManager in this thread. 3112038SN/A // There has been a recursive call to ensureLogManagerInitialized(). 3124935Sjrose final boolean isRecursiveInitialization = (initializedCalled == true); 3132038SN/A 3142038SN/A assert initializedCalled || !initializationDone 3152038SN/A : "Initialization can't be done if initialized has not been called!"; 3162038SN/A 3172038SN/A if (isRecursiveInitialization || initializationDone) { 3182038SN/A // If isRecursiveInitialization is true it means that we're 3192038SN/A // already in the process of initializing the LogManager in 3202038SN/A // this thread. There has been a recursive call to 3212038SN/A // ensureLogManagerInitialized(). We should not proceed as 3222038SN/A // it would lead to infinite recursion. 3232038SN/A // 3242038SN/A // If initializationDone is true then it means the manager 3252038SN/A // has finished initializing; just return: we're done. 3262038SN/A return; 3272038SN/A } 3282038SN/A // Calling addLogger below will in turn call requiresDefaultLogger() 3292038SN/A // which will call ensureLogManagerInitialized(). 3302038SN/A // We use initializedCalled to break the recursion. 3312038SN/A initializedCalled = true; 3322038SN/A try { 3332038SN/A AccessController.doPrivileged(new PrivilegedAction<Object>() { 3342038SN/A @Override 3352038SN/A public Object run() { 3362038SN/A assert rootLogger == null; 3372038SN/A assert initializedCalled && !initializationDone; 3382038SN/A 3392038SN/A // Read configuration. 3403529SN/A owner.readPrimordialConfiguration(); 3413529SN/A 3423529SN/A // Create and retain Logger for the root of the namespace. 3433529SN/A owner.rootLogger = owner.new RootLogger(); 3443529SN/A owner.addLogger(owner.rootLogger); 3453529SN/A if (!owner.rootLogger.isLevelInitialized()) { 3463529SN/A owner.rootLogger.setLevel(defaultLevel); 3473529SN/A } 3484183Sjrose 3494183Sjrose // Adding the global Logger. 3504183Sjrose // Do not call Logger.getGlobal() here as this might trigger 3513529SN/A // subtle inter-dependency issues. 3523529SN/A @SuppressWarnings("deprecation") 3533529SN/A final Logger global = Logger.global; 3543529SN/A 3553529SN/A // Make sure the global logger will be registered in the 3563529SN/A // global manager 3573529SN/A owner.addLogger(global); 3583529SN/A return null; 3593529SN/A } 3603529SN/A }); 3613529SN/A } finally { 3624935Sjrose initializationDone = true; 3633529SN/A } 3643529SN/A } 3653529SN/A } 3663529SN/A 3673529SN/A /** 3683529SN/A * Returns the global LogManager object. 3694935Sjrose * @return the global LogManager object 3703529SN/A */ 3713529SN/A public static LogManager getLogManager() { 3723529SN/A if (manager != null) { 3733529SN/A manager.ensureLogManagerInitialized(); 3743529SN/A } 3753529SN/A return manager; 3763529SN/A } 3778316Sjrose 3788316Sjrose private void readPrimordialConfiguration() { 3798316Sjrose if (!readPrimordialConfiguration) { 3808316Sjrose synchronized (this) { 3818316Sjrose if (!readPrimordialConfiguration) { 3823529SN/A // If System.in/out/err are null, it's a good 3834935Sjrose // indication that we're still in the 3844935Sjrose // bootstrapping phase 3853529SN/A if (System.out == null) { 3862038SN/A return; 3872038SN/A } 3882038SN/A readPrimordialConfiguration = true; 3892038SN/A 3902038SN/A try { 3912038SN/A AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { 3922038SN/A @Override 3934935Sjrose public Void run() throws Exception { 3942038SN/A readConfiguration(); 3952040SN/A 3964251Sjrose // Platform loggers begin to delegate to java.util.logging.Logger 3972038SN/A sun.util.logging.PlatformLogger.redirectPlatformLoggers(); 3984383Sjrose return null; 3994383Sjrose } 4004383Sjrose }); 4014383Sjrose } catch (Exception ex) { 4024383Sjrose assert false : "Exception raised while reading logging configuration: " + ex; 4034383Sjrose } 4044383Sjrose } 4052038SN/A } 4062038SN/A } 4072038SN/A } 4082038SN/A 4092038SN/A // LoggerContext maps from AppContext 4105688Stwisti private WeakHashMap<Object, LoggerContext> contextsMap = null; 4115688Stwisti 41213901Salanb // Returns the LoggerContext for the user code (i.e. application or AppContext). 4132040SN/A // Loggers are isolated from each AppContext. 4142038SN/A private LoggerContext getUserContext() { 4152038SN/A LoggerContext context = null; 4162038SN/A 4172038SN/A SecurityManager sm = System.getSecurityManager(); 4182431SN/A JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess(); 4192038SN/A if (sm != null && javaAwtAccess != null) { 4204935Sjrose // for each applet, it has its own LoggerContext isolated from others 4212038SN/A synchronized (javaAwtAccess) { 4228314Sjrose // find the AppContext of the applet code 4232038SN/A // will be null if we are in the main app context. 4242038SN/A final Object ecx = javaAwtAccess.getAppletContext(); 4252038SN/A if (ecx != null) { 4265688Stwisti if (contextsMap == null) { 4272038SN/A contextsMap = new WeakHashMap<>(); 4282038SN/A } 4292038SN/A context = contextsMap.get(ecx); 4305688Stwisti if (context == null) { 4312038SN/A // Create a new LoggerContext for the applet. 4322038SN/A context = new LoggerContext(); 4332038SN/A contextsMap.put(ecx, context); 4342038SN/A } 4352038SN/A } 4362038SN/A } 4372038SN/A } 4382038SN/A // for standalone app, return userContext 4392038SN/A return context != null ? context : userContext; 4402038SN/A } 4412038SN/A 4422038SN/A // The system context. 4432038SN/A final LoggerContext getSystemContext() { 4442038SN/A return systemContext; 4452038SN/A } 4462431SN/A 4474383Sjrose private List<LoggerContext> contexts() { 4484383Sjrose List<LoggerContext> cxs = new ArrayList<>(); 4494383Sjrose cxs.add(getSystemContext()); 4505688Stwisti cxs.add(getUserContext()); 4515688Stwisti return cxs; 4525688Stwisti } 4535688Stwisti 4545688Stwisti // Find or create a specified logger instance. If a logger has 4555688Stwisti // already been created with the given name it is returned. 4564383Sjrose // Otherwise a new logger instance is created and registered 4572431SN/A // in the LogManager global namespace. 4582038SN/A // This method will always return a non-null Logger object. 4592431SN/A // Synchronization is not required here. All synchronization for 4602038SN/A // adding a new Logger object is handled by addLogger(). 4615688Stwisti // 4625688Stwisti // This method must delegate to the LogManager implementation to 4635688Stwisti // add a new Logger or return the one that has been added previously 4645688Stwisti // as a LogManager subclass may override the addLogger, getLogger, 4652038SN/A // readConfiguration, and other methods. 4662038SN/A Logger demandLogger(String name, String resourceBundleName, Class<?> caller) { 4672038SN/A Logger result = getLogger(name); 4682038SN/A if (result == null) { 4694935Sjrose // only allocate the new logger once 4702038SN/A Logger newLogger = new Logger(name, resourceBundleName, caller, this, false); 4712431SN/A do { 4722038SN/A if (addLogger(newLogger)) { 4732038SN/A // We successfully added the new Logger that we 4742038SN/A // created above so return it without refetching. 47513111Smhaupt return newLogger; 4763012SN/A } 4772038SN/A 4782038SN/A // We didn't add the new Logger that we created above 4792431SN/A // because another thread added a Logger with the same 4802431SN/A // name after our null check above and before our call 4812038SN/A // to addLogger(). We have to refetch the Logger because 4822038SN/A // addLogger() returns a boolean instead of the Logger 4835685Sjrose // reference itself. However, if the thread that created 4842038SN/A // the other Logger is not holding a strong reference to 4852038SN/A // the other Logger, then it is possible for the other 4865688Stwisti // Logger to be GC'ed after we saw it in addLogger() and 4875688Stwisti // before we can refetch it. If it has been GC'ed then 4885688Stwisti // we'll just loop around and try again. 4895688Stwisti result = getLogger(name); 4905688Stwisti } while (result == null); 4912038SN/A } 4922038SN/A return result; 4932431SN/A } 4942431SN/A 4952431SN/A Logger demandSystemLogger(String name, String resourceBundleName) { 4962431SN/A // Add a system logger in the system context's namespace 4972431SN/A final Logger sysLogger = getSystemContext().demandLogger(name, resourceBundleName); 4982431SN/A 4995688Stwisti // Add the system logger to the LogManager's namespace if not exist 5005688Stwisti // so that there is only one single logger of the given name. 5012040SN/A // System loggers are visible to applications unless a logger of 5025688Stwisti // the same name has been added. 5032040SN/A Logger logger; 5042040SN/A do { 5055688Stwisti // First attempt to call addLogger instead of getLogger 5062431SN/A // This would avoid potential bug in custom LogManager.getLogger 5072431SN/A // implementation that adds a logger if does not exist 5082040SN/A if (addLogger(sysLogger)) { 5092431SN/A // successfully added the new system logger 5102431SN/A logger = sysLogger; 5112431SN/A } else { 5122431SN/A logger = getLogger(name); 5132431SN/A } 5142431SN/A } while (logger == null); 5152038SN/A 5162038SN/A // LogManager will set the sysLogger's handlers via LogManager.addLogger method. 5175688Stwisti if (logger != sysLogger && sysLogger.accessCheckedHandlers().length == 0) { 5185688Stwisti // if logger already exists but handlers not set 5195688Stwisti final Logger l = logger; 5205688Stwisti AccessController.doPrivileged(new PrivilegedAction<Void>() { 5215688Stwisti @Override 5225688Stwisti public Void run() { 5235688Stwisti for (Handler hdl : l.accessCheckedHandlers()) { 5248318Sjrose sysLogger.addHandler(hdl); 52512745Smartin } 5268314Sjrose return null; 5272038SN/A } 5282038SN/A }); 52912209Skshefov } 53012209Skshefov return sysLogger; 53112209Skshefov } 53212209Skshefov 5332038SN/A // LoggerContext maintains the logger namespace per context. 5342038SN/A // The default LogManager implementation has one system context and user 5352038SN/A // context. The system context is used to maintain the namespace for 5362038SN/A // all system loggers and is queried by the system code. If a system logger 5372038SN/A // doesn't exist in the user context, it'll also be added to the user context. 5382038SN/A // The user context is queried by the user code and all other loggers are 5395688Stwisti // added in the user context. 5405688Stwisti class LoggerContext { 5412038SN/A // Table of named Loggers that maps names to Loggers. 5422038SN/A private final Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>(); 5432038SN/A // Tree of named Loggers 5442038SN/A private final LogNode root; 5452038SN/A private LoggerContext() { 5462038SN/A this.root = new LogNode(null, this); 5472038SN/A } 5482038SN/A 5492038SN/A 5502038SN/A // Tells whether default loggers are required in this context. 5518314Sjrose // If true, the default loggers will be lazily added. 5528314Sjrose final boolean requiresDefaultLoggers() { 5535688Stwisti final boolean requiresDefaultLoggers = (getOwner() == manager); 5542038SN/A if (requiresDefaultLoggers) { 5552038SN/A getOwner().ensureLogManagerInitialized(); 5562038SN/A } 5572038SN/A return requiresDefaultLoggers; 5582038SN/A } 5592038SN/A 5602038SN/A // This context's LogManager. 5612038SN/A final LogManager getOwner() { 5622038SN/A return LogManager.this; 5632038SN/A } 5642038SN/A 5652038SN/A // This context owner's root logger, which if not null, and if 5665688Stwisti // the context requires default loggers, will be added to the context 5672040SN/A // logger's tree. 5682038SN/A final Logger getRootLogger() { 5693011SN/A return getOwner().rootLogger; 5702038SN/A } 5712431SN/A 5725688Stwisti // The global logger, which if not null, and if 5733530SN/A // the context requires default loggers, will be added to the context 5742038SN/A // logger's tree. 5758314Sjrose final Logger getGlobalLogger() { 5768314Sjrose @SuppressWarnings("deprecation") // avoids initialization cycles. 5778314Sjrose final Logger global = Logger.global; 5788314Sjrose return global; 5798314Sjrose } 5805688Stwisti 5812038SN/A Logger demandLogger(String name, String resourceBundleName) { 5822431SN/A // a LogManager subclass may have its own implementation to add and 5832431SN/A // get a Logger. So delegate to the LogManager to do the work. 5842038SN/A final LogManager owner = getOwner(); 5852038SN/A return owner.demandLogger(name, resourceBundleName, null); 5862038SN/A } 5872038SN/A 5882038SN/A 5895688Stwisti // Due to subtle deadlock issues getUserContext() no longer 5902038SN/A // calls addLocalLogger(rootLogger); 5912038SN/A // Therefore - we need to add the default loggers later on. 5923240SN/A // Checks that the context is properly initialized 5932038SN/A // This is necessary before calling e.g. find(name) 5942431SN/A // or getLoggerNames() 5952431SN/A // 5962038SN/A private void ensureInitialized() { 5972038SN/A if (requiresDefaultLoggers()) { 5988314Sjrose // Ensure that the root and global loggers are set. 5998314Sjrose ensureDefaultLogger(getRootLogger()); 6008314Sjrose ensureDefaultLogger(getGlobalLogger()); 6018314Sjrose } 6028314Sjrose } 6038314Sjrose 6048314Sjrose 6054245Sjrose synchronized Logger findLogger(String name) { 6064245Sjrose // ensure that this context is properly initialized before 6073240SN/A // looking for loggers. 6084245Sjrose ensureInitialized(); 6094245Sjrose LoggerWeakRef ref = namedLoggers.get(name); 6104245Sjrose if (ref == null) { 6114245Sjrose return null; 6124245Sjrose } 6134245Sjrose Logger logger = ref.get(); 6143240SN/A if (logger == null) { 6153240SN/A // Hashtable holds stale weak reference 6163240SN/A // to a logger which has been GC-ed. 6173240SN/A ref.dispose(); 6182038SN/A } 6192038SN/A return logger; 62012209Skshefov } 62112209Skshefov 62212209Skshefov // This method is called before adding a logger to the 62312209Skshefov // context. 6242038SN/A // 'logger' is the context that will be added. 6252038SN/A // This method will ensure that the defaults loggers are added 6262038SN/A // before adding 'logger'. 6272038SN/A // 6282038SN/A private void ensureAllDefaultLoggers(Logger logger) { 6292038SN/A if (requiresDefaultLoggers()) { 6302038SN/A final String name = logger.getName(); 6312038SN/A if (!name.isEmpty()) { 6322038SN/A ensureDefaultLogger(getRootLogger()); 6332038SN/A if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) { 6345688Stwisti ensureDefaultLogger(getGlobalLogger()); 6355688Stwisti } 6365688Stwisti } 6372038SN/A } 6388314Sjrose } 6398314Sjrose 6405688Stwisti private void ensureDefaultLogger(Logger logger) { 6415688Stwisti // Used for lazy addition of root logger and global logger 6422038SN/A // to a LoggerContext. 6432038SN/A 6442038SN/A // This check is simple sanity: we do not want that this 6452038SN/A // method be called for anything else than Logger.global 6462038SN/A // or owner.rootLogger. 6472038SN/A if (!requiresDefaultLoggers() || logger == null 6482038SN/A || logger != getGlobalLogger() && logger != LogManager.this.rootLogger ) { 6492038SN/A 6502038SN/A // the case where we have a non null logger which is neither 6512038SN/A // Logger.global nor manager.rootLogger indicates a serious 6528311Sjrose // issue - as ensureDefaultLogger should never be called 6538311Sjrose // with any other loggers than one of these two (or null - if 65412209Skshefov // e.g manager.rootLogger is not yet initialized)... 65512209Skshefov assert logger == null; 65612209Skshefov 65712209Skshefov return; 65815692Smhaupt } 6598311Sjrose 6608311Sjrose // Adds the logger if it's not already there. 66113901Salanb if (!namedLoggers.containsKey(logger.getName())) { 66213901Salanb // It is important to prevent addLocalLogger to 66313901Salanb // call ensureAllDefaultLoggers when we're in the process 66413901Salanb // off adding one of those default loggers - as this would 6658311Sjrose // immediately cause a stack overflow. 6668311Sjrose // Therefore we must pass addDefaultLoggersIfNeeded=false, 6678311Sjrose // even if requiresDefaultLoggers is true. 6688311Sjrose addLocalLogger(logger, false); 66913901Salanb } 6708311Sjrose } 6718311Sjrose 6722038SN/A boolean addLocalLogger(Logger logger) { 6732038SN/A // no need to add default loggers if it's not required 6742038SN/A return addLocalLogger(logger, requiresDefaultLoggers()); 6752038SN/A } 6762038SN/A 6772038SN/A // Add a logger to this context. This method will only set its level 6782038SN/A // and process parent loggers. It doesn't set its handlers. 6792038SN/A synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) { 6802038SN/A // addDefaultLoggersIfNeeded serves to break recursion when adding 6812038SN/A // default loggers. If we're adding one of the default loggers 6822038SN/A // (we're being called from ensureDefaultLogger()) then 6832038SN/A // addDefaultLoggersIfNeeded will be false: we don't want to 6848311Sjrose // call ensureAllDefaultLoggers again. 6858311Sjrose // 6868311Sjrose // Note: addDefaultLoggersIfNeeded can also be false when 6872038SN/A // requiresDefaultLoggers is false - since calling 6882038SN/A // ensureAllDefaultLoggers would have no effect in this case. 6892038SN/A if (addDefaultLoggersIfNeeded) { 6902040SN/A ensureAllDefaultLoggers(logger); 6912038SN/A } 6923011SN/A 6932038SN/A final String name = logger.getName(); 6942431SN/A if (name == null) { 6955688Stwisti throw new NullPointerException(); 6963530SN/A } 6972038SN/A LoggerWeakRef ref = namedLoggers.get(name); 6988314Sjrose if (ref != null) { 6998314Sjrose if (ref.get() == null) { 7008314Sjrose // It's possible that the Logger was GC'ed after a 7018314Sjrose // drainLoggerRefQueueBounded() call above so allow 7028314Sjrose // a new one to be registered. 7035688Stwisti ref.dispose(); 7042038SN/A } else { 7052431SN/A // We already have a registered logger with the given name. 7062431SN/A return false; 7072038SN/A } 7082038SN/A } 7092038SN/A 7102038SN/A // We're adding a new logger. 7115688Stwisti // Note that we are creating a weak reference here. 7125688Stwisti final LogManager owner = getOwner(); 7135688Stwisti logger.setLogManager(owner); 7145688Stwisti ref = owner.new LoggerWeakRef(logger); 7155688Stwisti namedLoggers.put(name, ref); 7165688Stwisti 7175688Stwisti // Apply any initial level defined for the new logger, unless 7185688Stwisti // the logger's level is already initialized 7195688Stwisti Level level = owner.getLevelProperty(name + ".level", null); 7202040SN/A if (level != null && !logger.isLevelInitialized()) { 7212431SN/A doSetLevel(logger, level); 7223240SN/A } 7232038SN/A 7245688Stwisti // instantiation of the handler is done in the LogManager.addLogger 7252038SN/A // implementation as a handler class may be only visible to LogManager 7268311Sjrose // subclass for the custom log manager case 7278311Sjrose processParentHandlers(logger, name); 7288311Sjrose 7298311Sjrose // Find the new node and its parent. 7308311Sjrose LogNode node = getNode(name); 7318311Sjrose node.loggerRef = ref; 7328311Sjrose Logger parent = null; 7338311Sjrose LogNode nodep = node.parent; 7348311Sjrose while (nodep != null) { 7358311Sjrose LoggerWeakRef nodeRef = nodep.loggerRef; 7368311Sjrose if (nodeRef != null) { 7378311Sjrose parent = nodeRef.get(); 7388311Sjrose if (parent != null) { 7398311Sjrose break; 7408311Sjrose } 7412431SN/A } 7422431SN/A nodep = nodep.parent; 7432038SN/A } 7442038SN/A 7452038SN/A if (parent != null) { 7462038SN/A doSetParent(logger, parent); 74712209Skshefov } 74812209Skshefov // Walk over the children and tell them we are their new parent. 74912209Skshefov node.walkAndSetParent(logger); 75012209Skshefov // new LogNode is ready so tell the LoggerWeakRef about it 7512038SN/A ref.setNode(node); 7522038SN/A return true; 75313111Smhaupt } 75413111Smhaupt 75513111Smhaupt synchronized void removeLoggerRef(String name, LoggerWeakRef ref) { 75613111Smhaupt namedLoggers.remove(name, ref); 7572431SN/A } 7582431SN/A 7592431SN/A synchronized Enumeration<String> getLoggerNames() { 7608314Sjrose // ensure that this context is properly initialized before 7612431SN/A // returning logger names. 7628314Sjrose ensureInitialized(); 7632431SN/A return namedLoggers.keys(); 76413111Smhaupt } 7652431SN/A 7662038SN/A // If logger.getUseParentHandlers() returns 'true' and any of the logger's 7672038SN/A // parents have levels or handlers defined, make sure they are instantiated. 7682431SN/A private void processParentHandlers(final Logger logger, final String name) { 76913111Smhaupt final LogManager owner = getOwner(); 7705688Stwisti AccessController.doPrivileged(new PrivilegedAction<Void>() { 7715688Stwisti @Override 7725688Stwisti public Void run() { 7735688Stwisti if (logger != owner.rootLogger) { 7745688Stwisti boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true); 7755688Stwisti if (!useParent) { 7765688Stwisti logger.setUseParentHandlers(false); 7775688Stwisti } 77813111Smhaupt } 77913111Smhaupt return null; 78013111Smhaupt } 78113111Smhaupt }); 78213111Smhaupt 7832038SN/A int ix = 1; 7842431SN/A for (;;) { 7852431SN/A int ix2 = name.indexOf('.', ix); 7862038SN/A if (ix2 < 0) { 7875688Stwisti break; 7882040SN/A } 7898314Sjrose String pname = name.substring(0, ix2); 7908314Sjrose if (owner.getProperty(pname + ".level") != null || 7918314Sjrose owner.getProperty(pname + ".handlers") != null) { 7922038SN/A // This pname has a level/handlers definition. 7933011SN/A // Make sure it exists. 7942038SN/A demandLogger(pname, null); 7952431SN/A } 7968314Sjrose ix = ix2+1; 7978314Sjrose } 7983530SN/A } 7992038SN/A 8008314Sjrose // Gets a node in our tree of logger nodes. 8018314Sjrose // If necessary, create it. 8028314Sjrose LogNode getNode(String name) { 8038314Sjrose if (name == null || name.equals("")) { 8048314Sjrose return root; 8058314Sjrose } 8068314Sjrose LogNode node = root; 8075688Stwisti while (name.length() > 0) { 8082038SN/A int ix = name.indexOf('.'); 8092431SN/A String head; 8102431SN/A if (ix > 0) { 8112431SN/A head = name.substring(0, ix); 8122431SN/A name = name.substring(ix + 1); 8132038SN/A } else { 8142038SN/A head = name; 8152038SN/A name = ""; 8162431SN/A } 8172431SN/A if (node.children == null) { 8182431SN/A node.children = new HashMap<>(); 8192040SN/A } 8205688Stwisti LogNode child = node.children.get(head); 8212038SN/A if (child == null) { 8222038SN/A child = new LogNode(node, this); 8233240SN/A node.children.put(head, child); 8242038SN/A } 8252038SN/A node = child; 8262038SN/A } 8272038SN/A return node; 8284383Sjrose } 82912209Skshefov } 83012209Skshefov 83112209Skshefov final class SystemLoggerContext extends LoggerContext { 83212209Skshefov // Add a system logger in the system context's namespace as well as 8334383Sjrose // in the LogManager's namespace if not exist so that there is only 8344383Sjrose // one single logger of the given name. System loggers are visible 8354383Sjrose // to applications unless a logger of the same name has been added. 8364383Sjrose @Override 8375688Stwisti Logger demandLogger(String name, String resourceBundleName) { 8385688Stwisti Logger result = findLogger(name); 8395688Stwisti if (result == null) { 8405688Stwisti // only allocate the new system logger once 8414383Sjrose Logger newLogger = new Logger(name, resourceBundleName, null, getOwner(), true); 8425688Stwisti do { 8435688Stwisti if (addLocalLogger(newLogger)) { 8444383Sjrose // We successfully added the new Logger that we 8454383Sjrose // created above so return it without refetching. 8464383Sjrose result = newLogger; 8474383Sjrose } else { 8484383Sjrose // We didn't add the new Logger that we created above 8494383Sjrose // because another thread added a Logger with the same 8504383Sjrose // name after our null check above and before our call 8514383Sjrose // to addLogger(). We have to refetch the Logger because 8524383Sjrose // addLogger() returns a boolean instead of the Logger 8534383Sjrose // reference itself. However, if the thread that created 8544383Sjrose // the other Logger is not holding a strong reference to 8554383Sjrose // the other Logger, then it is possible for the other 8568314Sjrose // Logger to be GC'ed after we saw it in addLogger() and 8574383Sjrose // before we can refetch it. If it has been GC'ed then 8584383Sjrose // we'll just loop around and try again. 8594383Sjrose result = findLogger(name); 8604383Sjrose } 8614383Sjrose } while (result == null); 8624383Sjrose } 8634383Sjrose return result; 8644383Sjrose } 8654383Sjrose } 8664383Sjrose 8674383Sjrose // Add new per logger handlers. 8684383Sjrose // We need to raise privilege here. All our decisions will 8694383Sjrose // be made based on the logging configuration, which can 8704383Sjrose // only be modified by trusted code. 8714383Sjrose private void loadLoggerHandlers(final Logger logger, final String name, 8724383Sjrose final String handlersPropertyName) 8734383Sjrose { 8744383Sjrose AccessController.doPrivileged(new PrivilegedAction<Object>() { 8752038SN/A @Override 87612209Skshefov public Object run() { 87712209Skshefov String names[] = parseClassNames(handlersPropertyName); 87812209Skshefov for (String word : names) { 87912209Skshefov try { 8802038SN/A Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word); 8812038SN/A Handler hdl = (Handler) clz.newInstance(); 8822038SN/A // Check if there is a property defining the 8832038SN/A // this handler's level. 8842038SN/A String levs = getProperty(word + ".level"); 8852038SN/A if (levs != null) { 8862038SN/A Level l = Level.findLevel(levs); 8872038SN/A if (l != null) { 8882038SN/A hdl.setLevel(l); 8892038SN/A } else { 8902038SN/A // Probably a bad level. Drop through. 8918314Sjrose System.err.println("Can't set level for " + word); 8928314Sjrose } 8932038SN/A } 8942038SN/A // Add this Handler to the logger 8952038SN/A logger.addHandler(hdl); 8962038SN/A } catch (Exception ex) { 8972038SN/A System.err.println("Can't load log handler \"" + word + "\""); 8982038SN/A System.err.println("" + ex); 8992038SN/A ex.printStackTrace(); 9002038SN/A } 9012038SN/A } 9022038SN/A return null; 9032038SN/A } 9042038SN/A }); 9052038SN/A } 9062038SN/A 9072040SN/A 9082038SN/A // loggerRefQueue holds LoggerWeakRef objects for Logger objects 9092038SN/A // that have been GC'ed. 9103011SN/A private final ReferenceQueue<Logger> loggerRefQueue 9112038SN/A = new ReferenceQueue<>(); 9122431SN/A 9135688Stwisti // Package-level inner class. 9143530SN/A // Helper class for managing WeakReferences to Logger objects. 9152038SN/A // 9168314Sjrose // LogManager.namedLoggers 9178314Sjrose // - has weak references to all named Loggers 9188314Sjrose // - namedLoggers keeps the LoggerWeakRef objects for the named 9198314Sjrose // Loggers around until we can deal with the book keeping for 9208314Sjrose // the named Logger that is being GC'ed. 9215688Stwisti // LogManager.LogNode.loggerRef 9222038SN/A // - has a weak reference to a named Logger 9232431SN/A // - the LogNode will also keep the LoggerWeakRef objects for 9242038SN/A // the named Loggers around; currently LogNodes never go away. 9252038SN/A // Logger.kids 9262038SN/A // - has a weak reference to each direct child Logger; this 9272038SN/A // includes anonymous and named Loggers 9282038SN/A // - anonymous Loggers are always children of the rootLogger 9292038SN/A // which is a strong reference; rootLogger.kids keeps the 9302038SN/A // LoggerWeakRef objects for the anonymous Loggers around 9312038SN/A // until we can deal with the book keeping. 9323240SN/A // 9332038SN/A final class LoggerWeakRef extends WeakReference<Logger> { 9342038SN/A private String name; // for namedLoggers cleanup 9352431SN/A private LogNode node; // for loggerRef cleanup 9362431SN/A private WeakReference<Logger> parentRef; // for kids cleanup 9372038SN/A private boolean disposed = false; // avoid calling dispose twice 9382038SN/A 9392038SN/A LoggerWeakRef(Logger logger) { 9402038SN/A super(logger, loggerRefQueue); 94112209Skshefov 94212209Skshefov name = logger.getName(); // save for namedLoggers cleanup 94312209Skshefov } 94412209Skshefov 9452038SN/A // dispose of this LoggerWeakRef object 9462038SN/A void dispose() { 9472038SN/A // Avoid calling dispose twice. When a Logger is gc'ed, its 9485688Stwisti // LoggerWeakRef will be enqueued. 9492038SN/A // However, a new logger of the same name may be added (or looked 9502038SN/A // up) before the queue is drained. When that happens, dispose() 9512038SN/A // will be called by addLocalLogger() or findLogger(). 9522038SN/A // Later when the queue is drained, dispose() will be called again 9532038SN/A // for the same LoggerWeakRef. Marking LoggerWeakRef as disposed 9542431SN/A // avoids processing the data twice (even though the code should 9552431SN/A // now be reentrant). 9562431SN/A synchronized(this) { 9572431SN/A // Note to maintainers: 9582038SN/A // Be careful not to call any method that tries to acquire 9592038SN/A // another lock from within this block - as this would surely 9602038SN/A // lead to deadlocks, given that dispose() can be called by 9612038SN/A // multiple threads, and from within different synchronized 9622038SN/A // methods/blocks. 9632038SN/A if (disposed) return; 9642038SN/A disposed = true; 9652038SN/A } 9662038SN/A 9675688Stwisti final LogNode n = node; 9685688Stwisti if (n != null) { 9695688Stwisti // n.loggerRef can only be safely modified from within 9702038SN/A // a lock on LoggerContext. removeLoggerRef is already 9712038SN/A // synchronized on LoggerContext so calling 9722038SN/A // n.context.removeLoggerRef from within this lock is safe. 9732038SN/A synchronized (n.context) { 9742431SN/A // if we have a LogNode, then we were a named Logger 9752038SN/A // so clear namedLoggers weak ref to us 9762038SN/A n.context.removeLoggerRef(name, this); 9772431SN/A name = null; // clear our ref to the Logger's name 9782431SN/A 9792431SN/A // LogNode may have been reused - so only clear 9802431SN/A // LogNode.loggerRef if LogNode.loggerRef == this 9812431SN/A if (n.loggerRef == this) { 9822431SN/A n.loggerRef = null; // clear LogNode's weak ref to us 9832431SN/A } 9842431SN/A node = null; // clear our ref to LogNode 9852038SN/A } 9865688Stwisti } 9872040SN/A 9888314Sjrose if (parentRef != null) { 9898314Sjrose // this LoggerWeakRef has or had a parent Logger 9908314Sjrose Logger parent = parentRef.get(); 9918314Sjrose if (parent != null) { 9925688Stwisti // the parent Logger is still there so clear the 9932038SN/A // parent Logger's weak ref to us 9943011SN/A parent.removeChildLogger(this); 9952431SN/A } 9962431SN/A parentRef = null; // clear our weak ref to the parent Logger 9972038SN/A } 9982431SN/A } 9992431SN/A 10008314Sjrose // set the node field to the specified value 10012431SN/A void setNode(LogNode node) { 10025688Stwisti this.node = node; 10033530SN/A } 10042038SN/A 10058314Sjrose // set the parentRef field to the specified value 10068314Sjrose void setParentRef(WeakReference<Logger> parentRef) { 10078314Sjrose this.parentRef = parentRef; 10085688Stwisti } 10092038SN/A } 10102431SN/A 10112431SN/A // Package-level method. 10122431SN/A // Drain some Logger objects that have been GC'ed. 10132431SN/A // 10142431SN/A // drainLoggerRefQueueBounded() is called by addLogger() below 10152431SN/A // and by Logger.getAnonymousLogger(String) so we'll drain up to 10162038SN/A // MAX_ITERATIONS GC'ed Loggers for every Logger we add. 10172038SN/A // 10182038SN/A // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives 10192431SN/A // us about a 50/50 mix in increased weak ref counts versus 10202038SN/A // decreased weak ref counts in the AnonLoggerWeakRefLeak test. 10212038SN/A // Here are stats for cleaning up sets of 400 anonymous Loggers: 10222431SN/A // - test duration 1 minute 10232038SN/A // - sample size of 125 sets of 400 10242040SN/A // - average: 1.99 ms 10252431SN/A // - minimum: 0.57 ms 10262431SN/A // - maximum: 25.3 ms 10272431SN/A // 10282431SN/A // The same config gives us a better decreased weak ref count 10292431SN/A // than increased weak ref count in the LoggerWeakRefLeak test. 10302431SN/A // Here are stats for cleaning up sets of 400 named Loggers: 10312431SN/A // - test duration 2 minutes 10322431SN/A // - sample size of 506 sets of 400 10332431SN/A // - average: 0.57 ms 10342038SN/A // - minimum: 0.02 ms 10352038SN/A // - maximum: 10.9 ms 10363240SN/A // 10372038SN/A private final static int MAX_ITERATIONS = 400; 10382431SN/A final void drainLoggerRefQueueBounded() { 10392431SN/A for (int i = 0; i < MAX_ITERATIONS; i++) { 10402431SN/A if (loggerRefQueue == null) { 10412431SN/A // haven't finished loading LogManager yet 10422431SN/A break; 10432431SN/A } 10442431SN/A 10452431SN/A LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll(); 10462431SN/A if (ref == null) { 10472038SN/A break; 10482038SN/A } 10492431SN/A // a Logger object has been GC'ed so clean it up 10502038SN/A ref.dispose(); 105112209Skshefov } 105212209Skshefov } 105312209Skshefov 105412209Skshefov /** 10552431SN/A * Add a named logger. This does nothing and returns false if a logger 10562038SN/A * with the same name is already registered. 10572431SN/A * <p> 10582431SN/A * The Logger factory methods call this method to register each 10592431SN/A * newly created Logger. 10602431SN/A * <p> 10612431SN/A * The application should retain its own reference to the Logger 10622431SN/A * object to avoid it being garbage collected. The LogManager 10632431SN/A * may only retain a weak reference. 10642038SN/A * 10652038SN/A * @param logger the new logger. 10662040SN/A * @return true if the argument logger was registered successfully, 10672040SN/A * false if a logger of that name already exists. 10682040SN/A * @exception NullPointerException if the logger name is null. 10692040SN/A */ 10702040SN/A public boolean addLogger(Logger logger) { 10712040SN/A final String name = logger.getName(); 10722040SN/A if (name == null) { 10732040SN/A throw new NullPointerException(); 10742040SN/A } 10752040SN/A drainLoggerRefQueueBounded(); 10762040SN/A LoggerContext cx = getUserContext(); 10772040SN/A if (cx.addLocalLogger(logger)) { 10782040SN/A // Do we have a per logger handler too? 10792040SN/A // Note: this will add a 200ms penalty 10802040SN/A loadLoggerHandlers(logger, name, name + ".handlers"); 10812040SN/A return true; 10822040SN/A } else { 10832040SN/A return false; 10842040SN/A } 10852040SN/A } 10862040SN/A 10872040SN/A // Private method to set a level on a logger. 10882040SN/A // If necessary, we raise privilege before doing the call. 10892040SN/A private static void doSetLevel(final Logger logger, final Level level) { 10902040SN/A SecurityManager sm = System.getSecurityManager(); 10914935Sjrose if (sm == null) { 10922040SN/A // There is no security manager, so things are easy. 10932040SN/A logger.setLevel(level); 10942040SN/A return; 10952040SN/A } 10962040SN/A // There is a security manager. Raise privilege before 10972040SN/A // calling setLevel. 10982040SN/A AccessController.doPrivileged(new PrivilegedAction<Object>() { 10992040SN/A @Override 11002040SN/A public Object run() { 11012040SN/A logger.setLevel(level); 11022040SN/A return null; 11032040SN/A }}); 11042040SN/A } 11052040SN/A 11062040SN/A // Private method to set a parent on a logger. 11072040SN/A // If necessary, we raise privilege before doing the setParent call. 11084183Sjrose private static void doSetParent(final Logger logger, final Logger parent) { 11092040SN/A SecurityManager sm = System.getSecurityManager(); 11104935Sjrose if (sm == null) { 11112040SN/A // There is no security manager, so things are easy. 11122040SN/A logger.setParent(parent); 11132040SN/A return; 11142040SN/A } 11154935Sjrose // There is a security manager. Raise privilege before 11162040SN/A // calling setParent. 11172040SN/A AccessController.doPrivileged(new PrivilegedAction<Object>() { 11182040SN/A @Override 11192040SN/A public Object run() { 11202040SN/A logger.setParent(parent); 11213530SN/A return null; 11222040SN/A }}); 11233530SN/A } 11243530SN/A 11253530SN/A /** 11262040SN/A * Method to find a named logger. 11272040SN/A * <p> 11282040SN/A * Note that since untrusted code may create loggers with 11293530SN/A * arbitrary names this method should not be relied on to 11303530SN/A * find Loggers for security sensitive logging. 11312040SN/A * It is also important to note that the Logger associated with the 11322040SN/A * String {@code name} may be garbage collected at any time if there 11332040SN/A * is no strong reference to the Logger. The caller of this method 11342040SN/A * must check the return value for null in order to properly handle 11355688Stwisti * the case where the Logger has been garbage collected. 11362435SN/A * 11372435SN/A * @param name name of the logger 11383530SN/A * @return matching logger or null if none is found 11392435SN/A */ 11403530SN/A public Logger getLogger(String name) { 11412435SN/A return getUserContext().findLogger(name); 11423530SN/A } 11432435SN/A 11442435SN/A /** 11452040SN/A * Get an enumeration of known logger names. 11462038SN/A * <p> 114712209Skshefov * Note: Loggers may be added dynamically as new classes are loaded. 114812209Skshefov * This method only reports on the loggers that are currently registered. 114912209Skshefov * It is also important to note that this method only returns the name 115012209Skshefov * of a Logger, not a strong reference to the Logger itself. 11515688Stwisti * The returned String does nothing to prevent the Logger from being 11522435SN/A * garbage collected. In particular, if the returned name is passed 11532435SN/A * to {@code LogManager.getLogger()}, then the caller must check the 11542435SN/A * return value from {@code LogManager.getLogger()} for null to properly 115512209Skshefov * handle the case where the Logger has been garbage collected in the 11562435SN/A * time since its name was returned by this method. 11572435SN/A * 115812209Skshefov * @return enumeration of logger name strings 115912209Skshefov */ 116012209Skshefov public Enumeration<String> getLoggerNames() { 116112209Skshefov return getUserContext().getLoggerNames(); 11625688Stwisti } 11632435SN/A 11642435SN/A /** 11655688Stwisti * Reinitialize the logging properties and reread the logging configuration. 11662435SN/A * <p> 116712209Skshefov * The same rules are used for locating the configuration properties 11682435SN/A * as are used at startup. So normally the logging properties will 11692435SN/A * be re-read from the same file that was used at startup. 117012209Skshefov * <P> 117112209Skshefov * Any log level definitions in the new configuration file will be 117212209Skshefov * applied using Logger.setLevel(), if the target Logger exists. 117312209Skshefov * <p> 11745688Stwisti * Any {@linkplain #addConfigurationListener registered configuration 11752435SN/A * listener} will be invoked after the properties are read. 11763530SN/A * 11772435SN/A * @exception SecurityException if a security manager exists and if 117812209Skshefov * the caller does not have LoggingPermission("control"). 11792435SN/A * @exception IOException if there are IO problems reading the configuration. 11802038SN/A */ 11812040SN/A public void readConfiguration() throws IOException, SecurityException { 11823530SN/A checkPermission(); 11833530SN/A 11845685Sjrose // if a configuration class is specified, load it and use it. 11855685Sjrose String cname = System.getProperty("java.util.logging.config.class"); 11863530SN/A if (cname != null) { 11873530SN/A try { 11883530SN/A // Instantiate the named class. It is its constructor's 11893530SN/A // responsibility to initialize the logging configuration, by 11903530SN/A // calling readConfiguration(InputStream) with a suitable stream. 11913530SN/A try { 11923530SN/A Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname); 11933530SN/A clz.newInstance(); 11942040SN/A return; 11952040SN/A } catch (ClassNotFoundException ex) { 11963530SN/A Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname); 11973530SN/A clz.newInstance(); 11983530SN/A return; 11993530SN/A } 12003530SN/A } catch (Exception ex) { 12015685Sjrose System.err.println("Logging configuration class \"" + cname + "\" failed"); 12023530SN/A System.err.println("" + ex); 12034383Sjrose // keep going and useful config file. 12045685Sjrose } 12053530SN/A } 12065688Stwisti 12075685Sjrose String fname = System.getProperty("java.util.logging.config.file"); 12085688Stwisti if (fname == null) { 12095685Sjrose fname = System.getProperty("java.home"); 12103530SN/A if (fname == null) { 12113530SN/A throw new Error("Can't find java.home ??"); 12123530SN/A } 12133530SN/A File f = new File(fname, "conf"); 12143530SN/A f = new File(f, "logging.properties"); 12153530SN/A fname = f.getCanonicalPath(); 12163530SN/A } 12173530SN/A try (final InputStream in = new FileInputStream(fname)) { 12183530SN/A final BufferedInputStream bin = new BufferedInputStream(in); 12193530SN/A readConfiguration(bin); 12203530SN/A } 12213530SN/A } 12223530SN/A 12233530SN/A /** 12243530SN/A * Reset the logging configuration. 12253530SN/A * <p> 12263530SN/A * For all named loggers, the reset operation removes and closes 12273530SN/A * all Handlers and (except for the root logger) sets the level 12283530SN/A * to null. The root logger's level is set to Level.INFO. 12293530SN/A * 12303530SN/A * @exception SecurityException if a security manager exists and if 12313530SN/A * the caller does not have LoggingPermission("control"). 12322435SN/A */ 12333530SN/A 12345685Sjrose public void reset() throws SecurityException { 12353530SN/A checkPermission(); 12363530SN/A synchronized (this) { 12373530SN/A props = new Properties(); 12383530SN/A // Since we are doing a reset we no longer want to initialize 12393530SN/A // the global handlers, if they haven't been initialized yet. 12403530SN/A initializedGlobalHandlers = true; 12412040SN/A } 12423530SN/A for (LoggerContext cx : contexts()) { 12433530SN/A Enumeration<String> enum_ = cx.getLoggerNames(); 12443530SN/A while (enum_.hasMoreElements()) { 12455688Stwisti String name = enum_.nextElement(); 12463530SN/A Logger logger = cx.findLogger(name); 12473530SN/A if (logger != null) { 12483530SN/A resetLogger(logger); 12493530SN/A } 12503530SN/A } 12513530SN/A } 12523530SN/A } 12533530SN/A 12543530SN/A // Private method to reset an individual target logger. 12553530SN/A private void resetLogger(Logger logger) { 12563530SN/A // Close all the Logger's handlers. 12573530SN/A Handler[] targets = logger.getHandlers(); 12583530SN/A for (Handler h : targets) { 12593530SN/A logger.removeHandler(h); 12603530SN/A try { 12618314Sjrose h.close(); 12628314Sjrose } catch (Exception ex) { 12638314Sjrose // Problems closing a handler? Keep going... 12648314Sjrose } 12658314Sjrose } 12665688Stwisti String name = logger.getName(); 12673530SN/A if (name != null && name.equals("")) { 12683530SN/A // This is the root logger. 12693530SN/A logger.setLevel(defaultLevel); 12703530SN/A } else { 12713530SN/A logger.setLevel(null); 12725685Sjrose } 12735685Sjrose } 12745685Sjrose 12753530SN/A // get a list of whitespace separated classnames from a property. 12763530SN/A private String[] parseClassNames(String propertyName) { 12773530SN/A String hands = getProperty(propertyName); 12782040SN/A if (hands == null) { 12795688Stwisti return new String[0]; 12802040SN/A } 12815685Sjrose hands = hands.trim(); 12825685Sjrose int ix = 0; 12835688Stwisti final List<String> result = new ArrayList<>(); 12845688Stwisti while (ix < hands.length()) { 12852040SN/A int end = ix; 12863530SN/A while (end < hands.length()) { 12873530SN/A if (Character.isWhitespace(hands.charAt(end))) { 12883530SN/A break; 12894251Sjrose } 12904251Sjrose if (hands.charAt(end) == ',') { 12913530SN/A break; 12923530SN/A } 12934251Sjrose end++; 12944251Sjrose } 12954251Sjrose String word = hands.substring(ix, end); 12963530SN/A ix = end+1; 12973530SN/A word = word.trim(); 12983530SN/A if (word.length() == 0) { 12993530SN/A continue; 13005685Sjrose } 13013530SN/A result.add(word); 13023530SN/A } 13033530SN/A return result.toArray(new String[result.size()]); 13045688Stwisti } 13055688Stwisti 13065688Stwisti /** 13075688Stwisti * Reinitialize the logging properties and reread the logging configuration 13085688Stwisti * from the given stream, which should be in java.util.Properties format. 13095688Stwisti * Any {@linkplain #addConfigurationListener registered configuration 13105688Stwisti * listener} will be invoked after the properties are read. 13115688Stwisti * <p> 13125685Sjrose * Any log level definitions in the new configuration file will be 13135685Sjrose * applied using Logger.setLevel(), if the target Logger exists. 13145685Sjrose * 13155685Sjrose * @param ins stream to read properties from 13165688Stwisti * @exception SecurityException if a security manager exists and if 13175688Stwisti * the caller does not have LoggingPermission("control"). 13185688Stwisti * @exception IOException if there are problems reading from the stream. 13195688Stwisti */ 13205688Stwisti public void readConfiguration(InputStream ins) throws IOException, SecurityException { 13215685Sjrose checkPermission(); 13223530SN/A reset(); 13233530SN/A 13243530SN/A // Load the properties 13253530SN/A props.load(ins); 13263530SN/A // Instantiate new configuration objects. 13273530SN/A String names[] = parseClassNames("config"); 13283530SN/A 13293530SN/A for (String word : names) { 13303530SN/A try { 13313530SN/A Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word); 13322040SN/A clz.newInstance(); 13333530SN/A } catch (Exception ex) { 13343530SN/A System.err.println("Can't load config class \"" + word + "\""); 13353530SN/A System.err.println("" + ex); 13365688Stwisti // ex.printStackTrace(); 13375688Stwisti } 13385688Stwisti } 13395688Stwisti 13405688Stwisti // Set levels on any pre-existing loggers, based on the new properties. 13415688Stwisti setLevelsOnExistingLoggers(); 13425688Stwisti 13435685Sjrose try { 13445685Sjrose invokeConfigurationListeners(); 13455685Sjrose } finally { 13465685Sjrose // Note that we need to reinitialize global handles when 13475688Stwisti // they are first referenced. 13485688Stwisti synchronized (this) { 13495688Stwisti initializedGlobalHandlers = false; 13505688Stwisti } 13515688Stwisti } 13525685Sjrose } 13533530SN/A 13543530SN/A /** 13553530SN/A * Get the value of a logging property. 13563530SN/A * The method returns null if the property is not found. 13573530SN/A * @param name property name 13582040SN/A * @return property value 13593530SN/A */ 13603530SN/A public String getProperty(String name) { 13613530SN/A return props.getProperty(name); 13625685Sjrose } 13635685Sjrose 13645685Sjrose // Package private method to get a String property. 13655685Sjrose // If the property is not defined we return the given 13665685Sjrose // default value. 13675685Sjrose String getStringProperty(String name, String defaultValue) { 13685685Sjrose String val = getProperty(name); 13695685Sjrose if (val == null) { 13702038SN/A return defaultValue; 13712038SN/A } 13722040SN/A return val.trim(); 13732038SN/A } 137412209Skshefov 137512209Skshefov // Package private method to get an integer property. 137612209Skshefov // If the property is not defined or cannot be parsed 137712209Skshefov // we return the given default value. 13785688Stwisti int getIntProperty(String name, int defaultValue) { 13792435SN/A String val = getProperty(name); 13802435SN/A if (val == null) { 13812435SN/A return defaultValue; 138212209Skshefov } 13832435SN/A try { 13842435SN/A return Integer.parseInt(val.trim()); 138512209Skshefov } catch (Exception ex) { 138612209Skshefov return defaultValue; 138712209Skshefov } 138812209Skshefov } 13895688Stwisti 13902435SN/A // Package private method to get a long property. 13912435SN/A // If the property is not defined or cannot be parsed 13925688Stwisti // we return the given default value. 13932435SN/A long getLongProperty(String name, long defaultValue) { 139412209Skshefov String val = getProperty(name); 13952435SN/A if (val == null) { 13962435SN/A return defaultValue; 139712209Skshefov } 139812209Skshefov try { 139912209Skshefov return Long.parseLong(val.trim()); 140012209Skshefov } catch (Exception ex) { 14015688Stwisti return defaultValue; 14022435SN/A } 14033530SN/A } 14042435SN/A 140512209Skshefov // Package private method to get a boolean property. 14062435SN/A // If the property is not defined or cannot be parsed 14072038SN/A // we return the given default value. 14082038SN/A boolean getBooleanProperty(String name, boolean defaultValue) { 14092040SN/A String val = getProperty(name); 14103530SN/A if (val == null) { 14113530SN/A return defaultValue; 14125685Sjrose } 14135685Sjrose val = val.toLowerCase(); 14143530SN/A if (val.equals("true") || val.equals("1")) { 14153530SN/A return true; 14163530SN/A } else if (val.equals("false") || val.equals("0")) { 14173530SN/A return false; 14183530SN/A } 14192040SN/A return defaultValue; 14202040SN/A } 14213530SN/A 14223530SN/A // Package private method to get a Level property. 14233530SN/A // If the property is not defined or cannot be parsed 14242038SN/A // we return the given default value. 14252038SN/A Level getLevelProperty(String name, Level defaultValue) { 14262040SN/A String val = getProperty(name); 14272038SN/A if (val == null) { 142812209Skshefov return defaultValue; 142912209Skshefov } 143012209Skshefov Level l = Level.findLevel(val.trim()); 143112209Skshefov return l != null ? l : defaultValue; 14325688Stwisti } 14332038SN/A 14342431SN/A // Package private method to get a filter property. 14352040SN/A // We return an instance of the class named by the "name" 14362040SN/A // property. If the property is not defined or has problems 14372040SN/A // we return the defaultValue. 14382040SN/A Filter getFilterProperty(String name, Filter defaultValue) { 143912209Skshefov String val = getProperty(name); 144012209Skshefov try { 144112209Skshefov if (val != null) { 144212209Skshefov Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val); 14435688Stwisti return (Filter) clz.newInstance(); 14442040SN/A } 14452431SN/A } catch (Exception ex) { 14462431SN/A // We got one of a variety of exceptions in creating the 14472431SN/A // class or creating an instance. 14485685Sjrose // Drop through. 14495685Sjrose } 14502431SN/A // We got an exception. Return the defaultValue. 14515685Sjrose return defaultValue; 14525685Sjrose } 14535685Sjrose 14545685Sjrose 14555685Sjrose // Package private method to get a formatter property. 145612209Skshefov // We return an instance of the class named by the "name" 145712209Skshefov // property. If the property is not defined or has problems 145812209Skshefov // we return the defaultValue. 145912209Skshefov Formatter getFormatterProperty(String name, Formatter defaultValue) { 14605688Stwisti String val = getProperty(name); 14615685Sjrose try { 14625685Sjrose if (val != null) { 14635685Sjrose Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val); 14645685Sjrose return (Formatter) clz.newInstance(); 14655685Sjrose } 14665685Sjrose } catch (Exception ex) { 14675685Sjrose // We got one of a variety of exceptions in creating the 14685685Sjrose // class or creating an instance. 14692038SN/A // Drop through. 14702038SN/A } 14715685Sjrose // We got an exception. Return the defaultValue. 14725685Sjrose return defaultValue; 14735685Sjrose } 14745685Sjrose 14755685Sjrose // Private method to load the global handlers. 14765685Sjrose // We do the real work lazily, when the global handlers 14775685Sjrose // are first used. 14785685Sjrose private synchronized void initializeGlobalHandlers() { 14795685Sjrose if (initializedGlobalHandlers) { 14805685Sjrose return; 14815685Sjrose } 14825685Sjrose 14835685Sjrose initializedGlobalHandlers = true; 14845685Sjrose 14855685Sjrose if (deathImminent) { 14865685Sjrose // Aaargh... 14875685Sjrose // The VM is shutting down and our exit hook has been called. 14885685Sjrose // Avoid allocating global handlers. 14895685Sjrose return; 14902040SN/A } 14912040SN/A loadLoggerHandlers(rootLogger, null, "handlers"); 14925685Sjrose } 14935685Sjrose 14945685Sjrose static final Permission controlPermission = new LoggingPermission("control", null); 14955685Sjrose 14965685Sjrose void checkPermission() { 14975685Sjrose SecurityManager sm = System.getSecurityManager(); 14985685Sjrose if (sm != null) 14995685Sjrose sm.checkPermission(controlPermission); 15005685Sjrose } 15015685Sjrose 15025685Sjrose /** 15035685Sjrose * Check that the current context is trusted to modify the logging 15045685Sjrose * configuration. This requires LoggingPermission("control"). 15055685Sjrose * <p> 15065685Sjrose * If the check fails we throw a SecurityException, otherwise 15075685Sjrose * we return normally. 15085685Sjrose * 15095685Sjrose * @exception SecurityException if a security manager exists and if 15105685Sjrose * the caller does not have LoggingPermission("control"). 15115685Sjrose */ 15125685Sjrose public void checkAccess() throws SecurityException { 15135685Sjrose checkPermission(); 15145685Sjrose } 15155685Sjrose 15165685Sjrose // Nested class to represent a node in our tree of named loggers. 15175685Sjrose private static class LogNode { 15182040SN/A HashMap<String,LogNode> children; 15192040SN/A LoggerWeakRef loggerRef; 15202040SN/A LogNode parent; 15212040SN/A final LoggerContext context; 15222040SN/A 15232040SN/A LogNode(LogNode parent, LoggerContext context) { 15242040SN/A this.parent = parent; 15252431SN/A this.context = context; 15265685Sjrose } 15274183Sjrose 15284251Sjrose // Recursive method to walk the tree below a node and set 15292431SN/A // a new parent logger. 15302040SN/A void walkAndSetParent(Logger parent) { 15312040SN/A if (children == null) { 15325685Sjrose return; 15332040SN/A } 15342040SN/A for (LogNode node : children.values()) { 15352040SN/A LoggerWeakRef ref = node.loggerRef; 15362040SN/A Logger logger = (ref == null) ? null : ref.get(); 15372040SN/A if (logger == null) { 15385685Sjrose node.walkAndSetParent(parent); 15395685Sjrose } else { 15405685Sjrose doSetParent(logger, parent); 15415685Sjrose } 15425685Sjrose } 15435685Sjrose } 15445685Sjrose } 15455685Sjrose 15465685Sjrose // We use a subclass of Logger for the root logger, so 15475685Sjrose // that we only instantiate the global handlers when they 15485685Sjrose // are first needed. 15492040SN/A private final class RootLogger extends Logger { 15502040SN/A private RootLogger() { 15512040SN/A // We do not call the protected Logger two args constructor here, 15522431SN/A // to avoid calling LogManager.getLogManager() from within the 15532431SN/A // RootLogger constructor. 15542431SN/A super("", null, null, LogManager.this, true); 15552431SN/A } 15562431SN/A 15572431SN/A @Override 15582040SN/A public void log(LogRecord record) { 15592040SN/A // Make sure that the global handlers have been instantiated. 15602040SN/A initializeGlobalHandlers(); 15612040SN/A super.log(record); 15622040SN/A } 15635685Sjrose 15645685Sjrose @Override 15655685Sjrose public void addHandler(Handler h) { 15665685Sjrose initializeGlobalHandlers(); 15675685Sjrose super.addHandler(h); 15685685Sjrose } 15695685Sjrose 15705685Sjrose @Override 15715685Sjrose public void removeHandler(Handler h) { 15725685Sjrose initializeGlobalHandlers(); 15735685Sjrose super.removeHandler(h); 15742040SN/A } 15752040SN/A 15762040SN/A @Override 15772040SN/A Handler[] accessCheckedHandlers() { 15785685Sjrose initializeGlobalHandlers(); 15795685Sjrose return super.accessCheckedHandlers(); 15805685Sjrose } 15815685Sjrose } 15825685Sjrose 15835685Sjrose 15845685Sjrose // Private method to be called when the configuration has 15855685Sjrose // changed to apply any level settings to any pre-existing loggers. 15865685Sjrose synchronized private void setLevelsOnExistingLoggers() { 15875685Sjrose Enumeration<?> enum_ = props.propertyNames(); 15885685Sjrose while (enum_.hasMoreElements()) { 15895685Sjrose String key = (String)enum_.nextElement(); 15905685Sjrose if (!key.endsWith(".level")) { 15915685Sjrose // Not a level definition. 15925685Sjrose continue; 15935685Sjrose } 15945685Sjrose int ix = key.length() - 6; 15955685Sjrose String name = key.substring(0, ix); 15962040SN/A Level level = getLevelProperty(key, null); 15972040SN/A if (level == null) { 15982040SN/A System.err.println("Bad level value for property: " + key); 15992040SN/A continue; 16004935Sjrose } 16012040SN/A for (LoggerContext cx : contexts()) { 16022040SN/A Logger l = cx.findLogger(name); 16032040SN/A if (l == null) { 16042038SN/A continue; 16052038SN/A } 16062038SN/A l.setLevel(level); 16072038SN/A } 16082038SN/A } 16092038SN/A } 16102038SN/A 16112038SN/A // Management Support 16122038SN/A private static LoggingMXBean loggingMXBean = null; 16132038SN/A /** 16142038SN/A * String representation of the 16152038SN/A * {@link javax.management.ObjectName} for the management interface 16162038SN/A * for the logging facility. 16172040SN/A * 16182040SN/A * @see java.lang.management.PlatformLoggingMXBean 16192038SN/A * @see java.util.logging.LoggingMXBean 16202038SN/A * 16212040SN/A * @since 1.5 16222038SN/A */ 16232038SN/A public final static String LOGGING_MXBEAN_NAME 16242038SN/A = "java.util.logging:type=Logging"; 16252038SN/A 16262038SN/A /** 16272038SN/A * Returns <tt>LoggingMXBean</tt> for managing loggers. 16282038SN/A * An alternative way to manage loggers is through the 16293011SN/A * {@link java.lang.management.PlatformLoggingMXBean} interface 16303011SN/A * that can be obtained by calling: 16314935Sjrose * <pre> 16323011SN/A * PlatformLoggingMXBean logging = {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class) 16333011SN/A * ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class); 16342038SN/A * </pre> 16352038SN/A * 16362038SN/A * @return a {@link LoggingMXBean} object. 16372038SN/A * 16382038SN/A * @see java.lang.management.PlatformLoggingMXBean 163912209Skshefov * @since 1.5 164012209Skshefov */ 164112209Skshefov public static synchronized LoggingMXBean getLoggingMXBean() { 164212209Skshefov if (loggingMXBean == null) { 16432038SN/A loggingMXBean = new Logging(); 16442038SN/A } 16452038SN/A return loggingMXBean; 16462038SN/A } 16472038SN/A 16482038SN/A /** 16492431SN/A * Adds a configuration listener to be invoked each time the logging 16502431SN/A * configuration is read. 16512038SN/A * If the listener is already registered the method does nothing. 16522038SN/A * <p> 16532038SN/A * The listener is invoked with privileges that are restricted by the 16544251Sjrose * calling context of this method. 16552038SN/A * The order in which the listeners are invoked is unspecified. 16562038SN/A * <p> 16574251Sjrose * It is recommended that listeners do not throw errors or exceptions. 16583240SN/A * 16592038SN/A * If a listener terminates with an uncaught error or exception then 16602038SN/A * the first exception will be propagated to the caller of 16612038SN/A * {@link #readConfiguration()} (or {@link #readConfiguration(java.io.InputStream)}) 16622038SN/A * after all listeners have been invoked. 16632038SN/A * 16642038SN/A * @implNote If more than one listener terminates with an uncaught error or 16652038SN/A * exception, an implementation may record the additional errors or 16662040SN/A * exceptions as {@linkplain Throwable#addSuppressed(java.lang.Throwable) 16672038SN/A * suppressed exceptions}. 16682038SN/A * 16692038SN/A * @param listener A configuration listener that will be invoked after the 16702038SN/A * configuration changed. 16712038SN/A * @return This LogManager. 16722038SN/A * @throws SecurityException if a security manager exists and if the 16732038SN/A * caller does not have LoggingPermission("control"). 16742038SN/A * @throws NullPointerException if the listener is null. 16753240SN/A * 16762038SN/A * @since 1.9 16772038SN/A */ 16782038SN/A public LogManager addConfigurationListener(Runnable listener) { 16792038SN/A final Runnable r = Objects.requireNonNull(listener); 16802038SN/A checkPermission(); 16812038SN/A final SecurityManager sm = System.getSecurityManager(); 16822038SN/A final AccessControlContext acc = 16832038SN/A sm == null ? null : AccessController.getContext(); 16842038SN/A final PrivilegedAction<Void> pa = 16854251Sjrose acc == null ? null : () -> { r.run() ; return null; }; 16865774Sjrose final Runnable pr = 16872038SN/A acc == null ? r : () -> AccessController.doPrivileged(pa, acc); 16882038SN/A // Will do nothing if already registered. 16892431SN/A listeners.putIfAbsent(r, pr); 16902038SN/A return this; 16912038SN/A } 16922038SN/A 16932038SN/A /** 16942038SN/A * Removes a previously registered configuration listener. 16952038SN/A * 16962038SN/A * Returns silently if the listener is not found. 16973240SN/A * 16982038SN/A * @param listener the configuration listener to remove. 16992038SN/A * @throws NullPointerException if the listener is null. 17002431SN/A * @throws SecurityException if a security manager exists and if the 17012431SN/A * caller does not have LoggingPermission("control"). 17022038SN/A * 17032038SN/A * @since 1.9 17042038SN/A */ 17053528SN/A public void removeConfigurationListener(Runnable listener) { 170612209Skshefov final Runnable key = Objects.requireNonNull(listener); 170712209Skshefov checkPermission(); 170812209Skshefov listeners.remove(key); 170912209Skshefov } 17105688Stwisti 17115688Stwisti private void invokeConfigurationListeners() { 17123528SN/A Throwable t = null; 17133528SN/A 17143528SN/A // We're using an IdentityHashMap because we want to compare 17153528SN/A // keys using identity (==). 17164251Sjrose // We don't want to loop within a block synchronized on 'listeners' 17174251Sjrose // to avoid invoking listeners from yet another synchronized block. 17184935Sjrose // So we're taking a snapshot of the values list to avoid the risk of 17194251Sjrose // ConcurrentModificationException while looping. 17204251Sjrose // 17213528SN/A for (Runnable c : listeners.values().toArray(new Runnable[0])) { 17223528SN/A try { 17233528SN/A c.run(); 172412209Skshefov } catch (ThreadDeath death) { 17252040SN/A throw death; 172612209Skshefov } catch (Error | RuntimeException x) { 172712209Skshefov if (t == null) t = x; 172812209Skshefov else t.addSuppressed(x); 172912209Skshefov } 17302040SN/A } 17312040SN/A // Listeners are not supposed to throw exceptions, but if that 17324383Sjrose // happens, we will rethrow the first error or exception that is raised 17334383Sjrose // after all listeners have been invoked. 17342040SN/A if (t instanceof Error) throw (Error)t; 17354251Sjrose if (t instanceof RuntimeException) throw (RuntimeException)t; 17362040SN/A } 17372040SN/A 17382431SN/A} 17392040SN/A