LogManager.java revision 49965c1dc9da104344f4893a05e45795a5740d20
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.  Oracle designates this
9 * particular file as subject to the "Classpath" exception as provided
10 * by Oracle in the LICENSE file that accompanied this code.
11 *
12 * This code is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 * version 2 for more details (a copy is included in the LICENSE file that
16 * accompanied this code).
17 *
18 * You should have received a copy of the GNU General Public License version
19 * 2 along with this work; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23 * or visit www.oracle.com if you need additional information or have any
24 * questions.
25 */
26
27
28package java.util.logging;
29
30import java.io.*;
31import java.util.*;
32import java.security.*;
33import java.lang.ref.ReferenceQueue;
34import java.lang.ref.WeakReference;
35
36import java.beans.PropertyChangeListener;
37import java.beans.PropertyChangeSupport;
38
39/**
40 * There is a single global LogManager object that is used to
41 * maintain a set of shared state about Loggers and log services.
42 * <p>
43 * This LogManager object:
44 * <ul>
45 * <li> Manages a hierarchical namespace of Logger objects.  All
46 *      named Loggers are stored in this namespace.
47 * <li> Manages a set of logging control properties.  These are
48 *      simple key-value pairs that can be used by Handlers and
49 *      other logging objects to configure themselves.
50 * </ul>
51 * <p>
52 * The global LogManager object can be retrieved using LogManager.getLogManager().
53 * The LogManager object is created during class initialization and
54 * cannot subsequently be changed.
55 * <p>
56 * At startup the LogManager class is located using the
57 * java.util.logging.manager system property.
58 * <p>
59 * By default, the LogManager reads its initial configuration from
60 * a properties file "lib/logging.properties" in the JRE directory.
61 * If you edit that property file you can change the default logging
62 * configuration for all uses of that JRE.
63 * <p>
64 * In addition, the LogManager uses two optional system properties that
65 * allow more control over reading the initial configuration:
66 * <ul>
67 * <li>"java.util.logging.config.class"
68 * <li>"java.util.logging.config.file"
69 * </ul>
70 * These two properties may be set via the Preferences API, or as
71 * command line property definitions to the "java" command, or as
72 * system property definitions passed to JNI_CreateJavaVM.
73 * <p>
74 * If the "java.util.logging.config.class" property is set, then the
75 * property value is treated as a class name.  The given class will be
76 * loaded, an object will be instantiated, and that object's constructor
77 * is responsible for reading in the initial configuration.  (That object
78 * may use other system properties to control its configuration.)  The
79 * alternate configuration class can use <tt>readConfiguration(InputStream)</tt>
80 * to define properties in the LogManager.
81 * <p>
82 * If "java.util.logging.config.class" property is <b>not</b> set,
83 * then the "java.util.logging.config.file" system property can be used
84 * to specify a properties file (in java.util.Properties format). The
85 * initial logging configuration will be read from this file.
86 * <p>
87 * If neither of these properties is defined then, as described
88 * above, the LogManager will read its initial configuration from
89 * a properties file "lib/logging.properties" in the JRE directory.
90 * <p>
91 * The properties for loggers and Handlers will have names starting
92 * with the dot-separated name for the handler or logger.
93 * <p>
94 * The global logging properties may include:
95 * <ul>
96 * <li>A property "handlers".  This defines a whitespace or comma separated
97 * list of class names for handler classes to load and register as
98 * handlers on the root Logger (the Logger named "").  Each class
99 * name must be for a Handler class which has a default constructor.
100 * Note that these Handlers may be created lazily, when they are
101 * first used.
102 *
103 * <li>A property "&lt;logger&gt;.handlers". This defines a whitespace or
104 * comma separated list of class names for handlers classes to
105 * load and register as handlers to the specified logger. Each class
106 * name must be for a Handler class which has a default constructor.
107 * Note that these Handlers may be created lazily, when they are
108 * first used.
109 *
110 * <li>A property "&lt;logger&gt;.useParentHandlers". This defines a boolean
111 * value. By default every logger calls its parent in addition to
112 * handling the logging message itself, this often result in messages
113 * being handled by the root logger as well. When setting this property
114 * to false a Handler needs to be configured for this logger otherwise
115 * no logging messages are delivered.
116 *
117 * <li>A property "config".  This property is intended to allow
118 * arbitrary configuration code to be run.  The property defines a
119 * whitespace or comma separated list of class names.  A new instance will be
120 * created for each named class.  The default constructor of each class
121 * may execute arbitrary code to update the logging configuration, such as
122 * setting logger levels, adding handlers, adding filters, etc.
123 * </ul>
124 * <p>
125 * Note that all classes loaded during LogManager configuration are
126 * first searched on the system class path before any user class path.
127 * That includes the LogManager class, any config classes, and any
128 * handler classes.
129 * <p>
130 * Loggers are organized into a naming hierarchy based on their
131 * dot separated names.  Thus "a.b.c" is a child of "a.b", but
132 * "a.b1" and a.b2" are peers.
133 * <p>
134 * All properties whose names end with ".level" are assumed to define
135 * log levels for Loggers.  Thus "foo.level" defines a log level for
136 * the logger called "foo" and (recursively) for any of its children
137 * in the naming hierarchy.  Log Levels are applied in the order they
138 * are defined in the properties file.  Thus level settings for child
139 * nodes in the tree should come after settings for their parents.
140 * The property name ".level" can be used to set the level for the
141 * root of the tree.
142 * <p>
143 * All methods on the LogManager object are multi-thread safe.
144 *
145 * @since 1.4
146*/
147
148public class LogManager {
149    // The global LogManager object
150    private static LogManager manager;
151
152    private Properties props = new Properties();
153    private PropertyChangeSupport changes
154                         = new PropertyChangeSupport(LogManager.class);
155    private final static Level defaultLevel = Level.INFO;
156
157    // LoggerContext for system loggers and user loggers
158    private final LoggerContext systemContext = new SystemLoggerContext();
159    private final LoggerContext userContext = new LoggerContext();
160    private Logger rootLogger;
161
162    // Have we done the primordial reading of the configuration file?
163    // (Must be done after a suitable amount of java.lang.System
164    // initialization has been done)
165    private volatile boolean readPrimordialConfiguration;
166    // Have we initialized global (root) handlers yet?
167    // This gets set to false in readConfiguration
168    private boolean initializedGlobalHandlers = true;
169    // True if JVM death is imminent and the exit hook has been called.
170    private boolean deathImminent;
171
172    static {
173        AccessController.doPrivileged(new PrivilegedAction<Object>() {
174                public Object run() {
175                    String cname = null;
176                    try {
177                        cname = System.getProperty("java.util.logging.manager");
178                        if (cname != null) {
179                                manager = (LogManager) getClassInstance(cname).newInstance();
180                        }
181                    } catch (Exception ex) {
182                        System.err.println("Could not load Logmanager \"" + cname + "\"");
183                        ex.printStackTrace();
184                    }
185                    if (manager == null) {
186                        manager = new LogManager();
187                    }
188
189                    // Create and retain Logger for the root of the namespace.
190                    manager.rootLogger = manager.new RootLogger();
191                    // since by design the global manager's userContext and
192                    // systemContext don't have their requiresDefaultLoggers
193                    // flag set - we make sure to add the root logger to
194                    // the global manager's default contexts here.
195                    manager.addLogger(manager.rootLogger);
196                    manager.systemContext.addLocalLogger(manager.rootLogger, false);
197                    manager.userContext.addLocalLogger(manager.rootLogger, false);
198
199                    // Adding the global Logger. Doing so in the Logger.<clinit>
200                    // would deadlock with the LogManager.<clinit>.
201                    Logger.global.setLogManager(manager);
202                    // Make sure the global logger will be registered in the
203                    // global manager's default contexts.
204                    manager.addLogger(Logger.global);
205                    manager.systemContext.addLocalLogger(Logger.global, false);
206                    manager.userContext.addLocalLogger(Logger.global, false);
207
208                    // We don't call readConfiguration() here, as we may be running
209                    // very early in the JVM startup sequence.  Instead readConfiguration
210                    // will be called lazily in getLogManager().
211                    return null;
212                }
213            });
214    }
215
216
217    // This private class is used as a shutdown hook.
218    // It does a "reset" to close all open handlers.
219    private class Cleaner extends Thread {
220
221        private Cleaner() {
222            /* Set context class loader to null in order to avoid
223             * keeping a strong reference to an application classloader.
224             */
225            this.setContextClassLoader(null);
226        }
227
228        public void run() {
229            // This is to ensure the LogManager.<clinit> is completed
230            // before synchronized block. Otherwise deadlocks are possible.
231            LogManager mgr = manager;
232
233            // If the global handlers haven't been initialized yet, we
234            // don't want to initialize them just so we can close them!
235            synchronized (LogManager.this) {
236                // Note that death is imminent.
237                deathImminent = true;
238                initializedGlobalHandlers = true;
239            }
240
241            // Do a reset to close all active handlers.
242            reset();
243        }
244    }
245
246
247    /**
248     * Protected constructor.  This is protected so that container applications
249     * (such as J2EE containers) can subclass the object.  It is non-public as
250     * it is intended that there only be one LogManager object, whose value is
251     * retrieved by calling Logmanager.getLogManager.
252     */
253    protected LogManager() {
254        // Add a shutdown hook to close the global handlers.
255        try {
256            Runtime.getRuntime().addShutdownHook(new Cleaner());
257        } catch (IllegalStateException e) {
258            // If the VM is already shutting down,
259            // We do not need to register shutdownHook.
260        }
261    }
262
263    /**
264     * Return the global LogManager object.
265     */
266    public static LogManager getLogManager() {
267        if (manager != null) {
268            manager.readPrimordialConfiguration();
269        }
270        return manager;
271    }
272
273    private void readPrimordialConfiguration() {
274        if (!readPrimordialConfiguration) {
275            synchronized (this) {
276                if (!readPrimordialConfiguration) {
277                    // If System.in/out/err are null, it's a good
278                    // indication that we're still in the
279                    // bootstrapping phase
280                    if (System.out == null) {
281                        return;
282                    }
283                    readPrimordialConfiguration = true;
284
285                    try {
286                        AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
287                                public Void run() throws Exception {
288                                    readConfiguration();
289
290                                    // Platform loggers begin to delegate to java.util.logging.Logger
291                                    sun.util.logging.PlatformLogger.redirectPlatformLoggers();
292                                    return null;
293                                }
294                            });
295                    } catch (Exception ex) {
296                        // System.err.println("Can't read logging configuration:");
297                        // ex.printStackTrace();
298                    }
299                }
300            }
301        }
302    }
303
304    /**
305     * Adds an event listener to be invoked when the logging
306     * properties are re-read. Adding multiple instances of
307     * the same event Listener results in multiple entries
308     * in the property event listener table.
309     *
310     * @param l  event listener
311     * @exception  SecurityException  if a security manager exists and if
312     *             the caller does not have LoggingPermission("control").
313     * @exception NullPointerException if the PropertyChangeListener is null.
314     */
315    public void addPropertyChangeListener(PropertyChangeListener l) throws SecurityException {
316        if (l == null) {
317            throw new NullPointerException();
318        }
319        checkPermission();
320        changes.addPropertyChangeListener(l);
321    }
322
323    /**
324     * Removes an event listener for property change events.
325     * If the same listener instance has been added to the listener table
326     * through multiple invocations of <CODE>addPropertyChangeListener</CODE>,
327     * then an equivalent number of
328     * <CODE>removePropertyChangeListener</CODE> invocations are required to remove
329     * all instances of that listener from the listener table.
330     * <P>
331     * Returns silently if the given listener is not found.
332     *
333     * @param l  event listener (can be null)
334     * @exception  SecurityException  if a security manager exists and if
335     *             the caller does not have LoggingPermission("control").
336     */
337    public void removePropertyChangeListener(PropertyChangeListener l) throws SecurityException {
338        checkPermission();
339        changes.removePropertyChangeListener(l);
340    }
341
342    // Returns the LoggerContext for the user code (i.e. application or AppContext).
343    // Loggers are isolated from each AppContext.
344    private LoggerContext getUserContext() {
345        // Android-changed: No AWT specific hooks.
346        return userContext;
347    }
348
349    private List<LoggerContext> contexts() {
350        List<LoggerContext> cxs = new ArrayList<>();
351        cxs.add(systemContext);
352        cxs.add(getUserContext());
353        return cxs;
354    }
355
356    // Find or create a specified logger instance. If a logger has
357    // already been created with the given name it is returned.
358    // Otherwise a new logger instance is created and registered
359    // in the LogManager global namespace.
360    // This method will always return a non-null Logger object.
361    // Synchronization is not required here. All synchronization for
362    // adding a new Logger object is handled by addLogger().
363    //
364    // This method must delegate to the LogManager implementation to
365    // add a new Logger or return the one that has been added previously
366    // as a LogManager subclass may override the addLogger, getLogger,
367    // readConfiguration, and other methods.
368    Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
369        Logger result = getLogger(name);
370        if (result == null) {
371            // only allocate the new logger once
372            Logger newLogger = new Logger(name, resourceBundleName, caller);
373            do {
374                if (addLogger(newLogger)) {
375                    // We successfully added the new Logger that we
376                    // created above so return it without refetching.
377                    return newLogger;
378                }
379
380                // We didn't add the new Logger that we created above
381                // because another thread added a Logger with the same
382                // name after our null check above and before our call
383                // to addLogger(). We have to refetch the Logger because
384                // addLogger() returns a boolean instead of the Logger
385                // reference itself. However, if the thread that created
386                // the other Logger is not holding a strong reference to
387                // the other Logger, then it is possible for the other
388                // Logger to be GC'ed after we saw it in addLogger() and
389                // before we can refetch it. If it has been GC'ed then
390                // we'll just loop around and try again.
391                result = getLogger(name);
392            } while (result == null);
393        }
394        return result;
395    }
396
397    Logger demandSystemLogger(String name, String resourceBundleName) {
398        // Add a system logger in the system context's namespace
399        final Logger sysLogger = systemContext.demandLogger(name, resourceBundleName);
400
401        // Add the system logger to the LogManager's namespace if not exist
402        // so that there is only one single logger of the given name.
403        // System loggers are visible to applications unless a logger of
404        // the same name has been added.
405        Logger logger;
406        do {
407            // First attempt to call addLogger instead of getLogger
408            // This would avoid potential bug in custom LogManager.getLogger
409            // implementation that adds a logger if does not exist
410            if (addLogger(sysLogger)) {
411                // successfully added the new system logger
412                logger = sysLogger;
413            } else {
414                logger = getLogger(name);
415            }
416        } while (logger == null);
417
418        // LogManager will set the sysLogger's handlers via LogManager.addLogger method.
419        if (logger != sysLogger && sysLogger.getHandlers().length == 0) {
420            // if logger already exists but handlers not set
421            final Logger l = logger;
422            AccessController.doPrivileged(new PrivilegedAction<Void>() {
423                public Void run() {
424                    for (Handler hdl : l.getHandlers()) {
425                        sysLogger.addHandler(hdl);
426                    }
427                    return null;
428                }
429            });
430        }
431        return sysLogger;
432    }
433
434    private static Class getClassInstance(String cname) {
435        Class clz = null;
436        if (cname != null) {
437            try {
438                clz = ClassLoader.getSystemClassLoader().loadClass(cname);
439            } catch (ClassNotFoundException ex) {
440                try {
441                    clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
442                } catch (ClassNotFoundException innerEx) {
443                    clz = null;
444                }
445            }
446        }
447        return clz;
448    }
449
450    // LoggerContext maintains the logger namespace per context.
451    // The default LogManager implementation has one system context and user
452    // context.  The system context is used to maintain the namespace for
453    // all system loggers and is queried by the system code.  If a system logger
454    // doesn't exist in the user context, it'll also be added to the user context.
455    // The user context is queried by the user code and all other loggers are
456    // added in the user context.
457    static class LoggerContext {
458        // Table of named Loggers that maps names to Loggers.
459        private final Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();
460        // Tree of named Loggers
461        private final LogNode root;
462        private final boolean requiresDefaultLoggers;
463        private LoggerContext() {
464            this(false);
465        }
466        private LoggerContext(boolean requiresDefaultLoggers) {
467            this.root = new LogNode(null, this);
468            this.requiresDefaultLoggers = requiresDefaultLoggers;
469        }
470
471        Logger demandLogger(String name, String resourceBundleName) {
472            // a LogManager subclass may have its own implementation to add and
473            // get a Logger.  So delegate to the LogManager to do the work.
474            return manager.demandLogger(name, resourceBundleName, null);
475        }
476
477
478        // Due to subtle deadlock issues getUserContext() no longer
479        // calls addLocalLogger(rootLogger);
480        // Therefore - we need to add the default loggers later on.
481        // Checks that the context is properly initialized
482        // This is necessary before calling e.g. find(name)
483        // or getLoggerNames()
484        //
485        private void ensureInitialized() {
486            if (requiresDefaultLoggers) {
487                // Ensure that the root and global loggers are set.
488                ensureDefaultLogger(manager.rootLogger);
489                ensureDefaultLogger(Logger.global);
490            }
491        }
492
493
494        synchronized Logger findLogger(String name) {
495            // ensure that this context is properly initialized before
496            // looking for loggers.
497            ensureInitialized();
498            LoggerWeakRef ref = namedLoggers.get(name);
499            if (ref == null) {
500                return null;
501            }
502            Logger logger = ref.get();
503            if (logger == null) {
504                // Hashtable holds stale weak reference
505                // to a logger which has been GC-ed.
506                removeLogger(name);
507            }
508            return logger;
509        }
510
511        // This method is called before adding a logger to the
512        // context.
513        // 'logger' is the context that will be added.
514        // This method will ensure that the defaults loggers are added
515        // before adding 'logger'.
516        //
517        private void ensureAllDefaultLoggers(Logger logger) {
518            if (requiresDefaultLoggers) {
519                final String name = logger.getName();
520                if (!name.isEmpty()) {
521                    ensureDefaultLogger(manager.rootLogger);
522                }
523                if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) {
524                    ensureDefaultLogger(Logger.global);
525                }
526            }
527        }
528
529        private void ensureDefaultLogger(Logger logger) {
530            // Used for lazy addition of root logger and global logger
531            // to a LoggerContext.
532
533            // This check is simple sanity: we do not want that this
534            // method be called for anything else than Logger.global
535            // or owner.rootLogger.
536            if (!requiresDefaultLoggers || logger == null
537                    || logger != Logger.global && logger != manager.rootLogger) {
538
539                // the case where we have a non null logger which is neither
540                // Logger.global nor manager.rootLogger indicates a serious
541                // issue - as ensureDefaultLogger should never be called
542                // with any other loggers than one of these two (or null - if
543                // e.g manager.rootLogger is not yet initialized)...
544                assert logger == null;
545
546                return;
547            }
548
549            // Adds the logger if it's not already there.
550            if (!namedLoggers.containsKey(logger.getName())) {
551                // It is important to prevent addLocalLogger to
552                // call ensureAllDefaultLoggers when we're in the process
553                // off adding one of those default loggers - as this would
554                // immediately cause a stack overflow.
555                // Therefore we must pass addDefaultLoggersIfNeeded=false,
556                // even if requiresDefaultLoggers is true.
557                addLocalLogger(logger, false);
558            }
559        }
560
561        boolean addLocalLogger(Logger logger) {
562            // no need to add default loggers if it's not required
563            return addLocalLogger(logger, requiresDefaultLoggers);
564        }
565
566        boolean addLocalLogger(Logger logger, LogManager manager) {
567            // no need to add default loggers if it's not required
568            return addLocalLogger(logger, requiresDefaultLoggers, manager);
569        }
570
571        boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) {
572            return addLocalLogger(logger, addDefaultLoggersIfNeeded, manager);
573        }
574
575        // Add a logger to this context.  This method will only set its level
576        // and process parent loggers.  It doesn't set its handlers.
577        synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded,
578                                            LogManager manager) {
579            // addDefaultLoggersIfNeeded serves to break recursion when adding
580            // default loggers. If we're adding one of the default loggers
581            // (we're being called from ensureDefaultLogger()) then
582            // addDefaultLoggersIfNeeded will be false: we don't want to
583            // call ensureAllDefaultLoggers again.
584            //
585            // Note: addDefaultLoggersIfNeeded can also be false when
586            //       requiresDefaultLoggers is false - since calling
587            //       ensureAllDefaultLoggers would have no effect in this case.
588            if (addDefaultLoggersIfNeeded) {
589                ensureAllDefaultLoggers(logger);
590            }
591
592            final String name = logger.getName();
593            if (name == null) {
594                throw new NullPointerException();
595            }
596
597            LoggerWeakRef ref = namedLoggers.get(name);
598            if (ref != null) {
599                if (ref.get() == null) {
600                    // It's possible that the Logger was GC'ed after a
601                    // drainLoggerRefQueueBounded() call so allow
602                    // a new one to be registered.
603                    removeLogger(name);
604                } else {
605                    // We already have a registered logger with the given name.
606                    return false;
607                }
608            }
609
610            // We're adding a new logger.
611            // Note that we are creating a weak reference here.
612            ref = manager.new LoggerWeakRef(logger);
613            namedLoggers.put(name, ref);
614
615            // Apply any initial level defined for the new logger.
616            Level level = manager.getLevelProperty(name + ".level", null);
617            if (level != null) {
618                doSetLevel(logger, level);
619            }
620
621            processParentHandlers(logger, name);
622
623            // Find the new node and its parent.
624            LogNode node = getNode(name);
625            node.loggerRef = ref;
626            Logger parent = null;
627            LogNode nodep = node.parent;
628            while (nodep != null) {
629                LoggerWeakRef nodeRef = nodep.loggerRef;
630                if (nodeRef != null) {
631                    parent = nodeRef.get();
632                    if (parent != null) {
633                        break;
634                    }
635                }
636                nodep = nodep.parent;
637            }
638
639            if (parent != null) {
640                doSetParent(logger, parent);
641            }
642            // Walk over the children and tell them we are their new parent.
643            node.walkAndSetParent(logger);
644            // new LogNode is ready so tell the LoggerWeakRef about it
645            ref.setNode(node);
646            return true;
647        }
648
649        // note: all calls to removeLogger are synchronized on LogManager's
650        // intrinsic lock
651        void removeLogger(String name) {
652            namedLoggers.remove(name);
653        }
654
655        synchronized Enumeration<String> getLoggerNames() {
656            // ensure that this context is properly initialized before
657            // returning logger names.
658            ensureInitialized();
659            return namedLoggers.keys();
660        }
661
662        // If logger.getUseParentHandlers() returns 'true' and any of the logger's
663        // parents have levels or handlers defined, make sure they are instantiated.
664        private void processParentHandlers(final Logger logger, final String name) {
665            AccessController.doPrivileged(new PrivilegedAction<Void>() {
666                public Void run() {
667                    if (logger != manager.rootLogger) {
668                        boolean useParent = manager.getBooleanProperty(name + ".useParentHandlers", true);
669                        if (!useParent) {
670                            logger.setUseParentHandlers(false);
671                        }
672                    }
673                    return null;
674                }
675            });
676
677            int ix = 1;
678            for (;;) {
679                int ix2 = name.indexOf(".", ix);
680                if (ix2 < 0) {
681                    break;
682                }
683                String pname = name.substring(0, ix2);
684                if (manager.getProperty(pname + ".level") != null ||
685                    manager.getProperty(pname + ".handlers") != null) {
686                    // This pname has a level/handlers definition.
687                    // Make sure it exists.
688                    demandLogger(pname, null);
689                }
690                ix = ix2+1;
691            }
692        }
693
694        // Gets a node in our tree of logger nodes.
695        // If necessary, create it.
696        LogNode getNode(String name) {
697            if (name == null || name.equals("")) {
698                return root;
699            }
700            LogNode node = root;
701            while (name.length() > 0) {
702                int ix = name.indexOf(".");
703                String head;
704                if (ix > 0) {
705                    head = name.substring(0, ix);
706                    name = name.substring(ix + 1);
707                } else {
708                    head = name;
709                    name = "";
710                }
711                if (node.children == null) {
712                    node.children = new HashMap<>();
713                }
714                LogNode child = node.children.get(head);
715                if (child == null) {
716                    child = new LogNode(node, this);
717                    node.children.put(head, child);
718                }
719                node = child;
720            }
721            return node;
722        }
723    }
724
725    static class SystemLoggerContext extends LoggerContext {
726        // Add a system logger in the system context's namespace as well as
727        // in the LogManager's namespace if not exist so that there is only
728        // one single logger of the given name.  System loggers are visible
729        // to applications unless a logger of the same name has been added.
730        Logger demandLogger(String name, String resourceBundleName) {
731            Logger result = findLogger(name);
732            if (result == null) {
733                // only allocate the new system logger once
734                Logger newLogger = new Logger(name, resourceBundleName);
735                do {
736                    if (addLocalLogger(newLogger)) {
737                        // We successfully added the new Logger that we
738                        // created above so return it without refetching.
739                        result = newLogger;
740                    } else {
741                        // We didn't add the new Logger that we created above
742                        // because another thread added a Logger with the same
743                        // name after our null check above and before our call
744                        // to addLogger(). We have to refetch the Logger because
745                        // addLogger() returns a boolean instead of the Logger
746                        // reference itself. However, if the thread that created
747                        // the other Logger is not holding a strong reference to
748                        // the other Logger, then it is possible for the other
749                        // Logger to be GC'ed after we saw it in addLogger() and
750                        // before we can refetch it. If it has been GC'ed then
751                        // we'll just loop around and try again.
752                        result = findLogger(name);
753                    }
754                } while (result == null);
755            }
756            return result;
757        }
758    }
759
760    // Add new per logger handlers.
761    // We need to raise privilege here. All our decisions will
762    // be made based on the logging configuration, which can
763    // only be modified by trusted code.
764    private void loadLoggerHandlers(final Logger logger, final String name,
765                                    final String handlersPropertyName)
766    {
767        AccessController.doPrivileged(new PrivilegedAction<Object>() {
768            public Object run() {
769                String names[] = parseClassNames(handlersPropertyName);
770                for (int i = 0; i < names.length; i++) {
771                    String word = names[i];
772                    try {
773                        Class clz = getClassInstance(word);
774                        Handler hdl = (Handler) clz.newInstance();
775                        // Check if there is a property defining the
776                        // this handler's level.
777                        String levs = getProperty(word + ".level");
778                        if (levs != null) {
779                            Level l = Level.findLevel(levs);
780                            if (l != null) {
781                                hdl.setLevel(l);
782                            } else {
783                                // Probably a bad level. Drop through.
784                                System.err.println("Can't set level for " + word);
785                            }
786                        }
787                        // Add this Handler to the logger
788                        logger.addHandler(hdl);
789                    } catch (Exception ex) {
790                        System.err.println("Can't load log handler \"" + word + "\"");
791                        System.err.println("" + ex);
792                        ex.printStackTrace();
793                    }
794                }
795                return null;
796            }
797        });
798    }
799
800
801    // loggerRefQueue holds LoggerWeakRef objects for Logger objects
802    // that have been GC'ed.
803    private final ReferenceQueue<Logger> loggerRefQueue
804        = new ReferenceQueue<>();
805
806    // Package-level inner class.
807    // Helper class for managing WeakReferences to Logger objects.
808    //
809    // LogManager.namedLoggers
810    //     - has weak references to all named Loggers
811    //     - namedLoggers keeps the LoggerWeakRef objects for the named
812    //       Loggers around until we can deal with the book keeping for
813    //       the named Logger that is being GC'ed.
814    // LogManager.LogNode.loggerRef
815    //     - has a weak reference to a named Logger
816    //     - the LogNode will also keep the LoggerWeakRef objects for
817    //       the named Loggers around; currently LogNodes never go away.
818    // Logger.kids
819    //     - has a weak reference to each direct child Logger; this
820    //       includes anonymous and named Loggers
821    //     - anonymous Loggers are always children of the rootLogger
822    //       which is a strong reference; rootLogger.kids keeps the
823    //       LoggerWeakRef objects for the anonymous Loggers around
824    //       until we can deal with the book keeping.
825    //
826    final class LoggerWeakRef extends WeakReference<Logger> {
827        private String                name;       // for namedLoggers cleanup
828        private LogNode               node;       // for loggerRef cleanup
829        private WeakReference<Logger> parentRef;  // for kids cleanup
830
831        LoggerWeakRef(Logger logger) {
832            super(logger, loggerRefQueue);
833
834            name = logger.getName();  // save for namedLoggers cleanup
835        }
836
837        // dispose of this LoggerWeakRef object
838        void dispose() {
839            if (node != null) {
840                // if we have a LogNode, then we were a named Logger
841                // so clear namedLoggers weak ref to us
842                node.context.removeLogger(name);
843                name = null;  // clear our ref to the Logger's name
844
845                node.loggerRef = null;  // clear LogNode's weak ref to us
846                node = null;            // clear our ref to LogNode
847            }
848
849            if (parentRef != null) {
850                // this LoggerWeakRef has or had a parent Logger
851                Logger parent = parentRef.get();
852                if (parent != null) {
853                    // the parent Logger is still there so clear the
854                    // parent Logger's weak ref to us
855                    parent.removeChildLogger(this);
856                }
857                parentRef = null;  // clear our weak ref to the parent Logger
858            }
859        }
860
861        // set the node field to the specified value
862        void setNode(LogNode node) {
863            this.node = node;
864        }
865
866        // set the parentRef field to the specified value
867        void setParentRef(WeakReference<Logger> parentRef) {
868            this.parentRef = parentRef;
869        }
870    }
871
872    // Package-level method.
873    // Drain some Logger objects that have been GC'ed.
874    //
875    // drainLoggerRefQueueBounded() is called by addLogger() below
876    // and by Logger.getAnonymousLogger(String) so we'll drain up to
877    // MAX_ITERATIONS GC'ed Loggers for every Logger we add.
878    //
879    // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives
880    // us about a 50/50 mix in increased weak ref counts versus
881    // decreased weak ref counts in the AnonLoggerWeakRefLeak test.
882    // Here are stats for cleaning up sets of 400 anonymous Loggers:
883    //   - test duration 1 minute
884    //   - sample size of 125 sets of 400
885    //   - average: 1.99 ms
886    //   - minimum: 0.57 ms
887    //   - maximum: 25.3 ms
888    //
889    // The same config gives us a better decreased weak ref count
890    // than increased weak ref count in the LoggerWeakRefLeak test.
891    // Here are stats for cleaning up sets of 400 named Loggers:
892    //   - test duration 2 minutes
893    //   - sample size of 506 sets of 400
894    //   - average: 0.57 ms
895    //   - minimum: 0.02 ms
896    //   - maximum: 10.9 ms
897    //
898    private final static int MAX_ITERATIONS = 400;
899    final synchronized void drainLoggerRefQueueBounded() {
900        for (int i = 0; i < MAX_ITERATIONS; i++) {
901            if (loggerRefQueue == null) {
902                // haven't finished loading LogManager yet
903                break;
904            }
905
906            LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll();
907            if (ref == null) {
908                break;
909            }
910            // a Logger object has been GC'ed so clean it up
911            ref.dispose();
912        }
913    }
914
915    /**
916     * Add a named logger.  This does nothing and returns false if a logger
917     * with the same name is already registered.
918     * <p>
919     * The Logger factory methods call this method to register each
920     * newly created Logger.
921     * <p>
922     * The application should retain its own reference to the Logger
923     * object to avoid it being garbage collected.  The LogManager
924     * may only retain a weak reference.
925     *
926     * @param   logger the new logger.
927     * @return  true if the argument logger was registered successfully,
928     *          false if a logger of that name already exists.
929     * @exception NullPointerException if the logger name is null.
930     */
931    public boolean addLogger(Logger logger) {
932        final String name = logger.getName();
933        if (name == null) {
934            throw new NullPointerException();
935        }
936        drainLoggerRefQueueBounded();
937        LoggerContext cx = getUserContext();
938        if (cx.addLocalLogger(logger, this)) {
939            // Do we have a per logger handler too?
940            // Note: this will add a 200ms penalty
941            loadLoggerHandlers(logger, name, name + ".handlers");
942            return true;
943        } else {
944            return false;
945        }
946    }
947
948    // Private method to set a level on a logger.
949    // If necessary, we raise privilege before doing the call.
950    private static void doSetLevel(final Logger logger, final Level level) {
951        SecurityManager sm = System.getSecurityManager();
952        if (sm == null) {
953            // There is no security manager, so things are easy.
954            logger.setLevel(level);
955            return;
956        }
957        // There is a security manager.  Raise privilege before
958        // calling setLevel.
959        AccessController.doPrivileged(new PrivilegedAction<Object>() {
960            public Object run() {
961                logger.setLevel(level);
962                return null;
963            }});
964    }
965
966    // Private method to set a parent on a logger.
967    // If necessary, we raise privilege before doing the setParent call.
968    private static void doSetParent(final Logger logger, final Logger parent) {
969        SecurityManager sm = System.getSecurityManager();
970        if (sm == null) {
971            // There is no security manager, so things are easy.
972            logger.setParent(parent);
973            return;
974        }
975        // There is a security manager.  Raise privilege before
976        // calling setParent.
977        AccessController.doPrivileged(new PrivilegedAction<Object>() {
978            public Object run() {
979                logger.setParent(parent);
980                return null;
981            }});
982    }
983
984    /**
985     * Method to find a named logger.
986     * <p>
987     * Note that since untrusted code may create loggers with
988     * arbitrary names this method should not be relied on to
989     * find Loggers for security sensitive logging.
990     * It is also important to note that the Logger associated with the
991     * String {@code name} may be garbage collected at any time if there
992     * is no strong reference to the Logger. The caller of this method
993     * must check the return value for null in order to properly handle
994     * the case where the Logger has been garbage collected.
995     * <p>
996     * @param name name of the logger
997     * @return  matching logger or null if none is found
998     */
999    public Logger getLogger(String name) {
1000        return getUserContext().findLogger(name);
1001    }
1002
1003    /**
1004     * Get an enumeration of known logger names.
1005     * <p>
1006     * Note:  Loggers may be added dynamically as new classes are loaded.
1007     * This method only reports on the loggers that are currently registered.
1008     * It is also important to note that this method only returns the name
1009     * of a Logger, not a strong reference to the Logger itself.
1010     * The returned String does nothing to prevent the Logger from being
1011     * garbage collected. In particular, if the returned name is passed
1012     * to {@code LogManager.getLogger()}, then the caller must check the
1013     * return value from {@code LogManager.getLogger()} for null to properly
1014     * handle the case where the Logger has been garbage collected in the
1015     * time since its name was returned by this method.
1016     * <p>
1017     * @return  enumeration of logger name strings
1018     */
1019    public Enumeration<String> getLoggerNames() {
1020        return getUserContext().getLoggerNames();
1021    }
1022
1023    /**
1024     * Reinitialize the logging properties and reread the logging configuration.
1025     * <p>
1026     * The same rules are used for locating the configuration properties
1027     * as are used at startup.  So normally the logging properties will
1028     * be re-read from the same file that was used at startup.
1029     * <P>
1030     * Any log level definitions in the new configuration file will be
1031     * applied using Logger.setLevel(), if the target Logger exists.
1032     * <p>
1033     * A PropertyChangeEvent will be fired after the properties are read.
1034     *
1035     * @exception  SecurityException  if a security manager exists and if
1036     *             the caller does not have LoggingPermission("control").
1037     * @exception  IOException if there are IO problems reading the configuration.
1038     */
1039    public void readConfiguration() throws IOException, SecurityException {
1040        checkPermission();
1041
1042        // if a configuration class is specified, load it and use it.
1043        String cname = System.getProperty("java.util.logging.config.class");
1044        if (cname != null) {
1045            try {
1046                // Instantiate the named class.  It is its constructor's
1047                // responsibility to initialize the logging configuration, by
1048                // calling readConfiguration(InputStream) with a suitable stream.
1049                getClassInstance(cname).newInstance();
1050                return;
1051            } catch (Exception ex) {
1052                System.err.println("Logging configuration class \"" + cname + "\" failed");
1053                System.err.println("" + ex);
1054                // keep going and useful config file.
1055            }
1056        }
1057
1058        String fname = System.getProperty("java.util.logging.config.file");
1059        if (fname == null) {
1060            fname = System.getProperty("java.home");
1061            if (fname == null) {
1062                throw new Error("Can't find java.home ??");
1063            }
1064            File f = new File(fname, "lib");
1065            f = new File(f, "logging.properties");
1066            fname = f.getCanonicalPath();
1067        }
1068
1069        // Android-changed: Look in the boot class-path jar files for the logging.properties.
1070        // It will not be present in the file system.
1071        InputStream in;
1072        try {
1073            in = new FileInputStream(fname);
1074        } catch (Exception e) {
1075            in = LogManager.class.getResourceAsStream("logging.properties");
1076            if (in == null) {
1077                throw e;
1078            }
1079        }
1080
1081        BufferedInputStream bin = new BufferedInputStream(in);
1082        try {
1083            readConfiguration(bin);
1084        } finally {
1085            if (in != null) {
1086                in.close();
1087            }
1088        }
1089    }
1090
1091    /**
1092     * Reset the logging configuration.
1093     * <p>
1094     * For all named loggers, the reset operation removes and closes
1095     * all Handlers and (except for the root logger) sets the level
1096     * to null.  The root logger's level is set to Level.INFO.
1097     *
1098     * @exception  SecurityException  if a security manager exists and if
1099     *             the caller does not have LoggingPermission("control").
1100     */
1101
1102    public void reset() throws SecurityException {
1103        checkPermission();
1104        synchronized (this) {
1105            props = new Properties();
1106            // Since we are doing a reset we no longer want to initialize
1107            // the global handlers, if they haven't been initialized yet.
1108            initializedGlobalHandlers = true;
1109        }
1110        for (LoggerContext cx : contexts()) {
1111            Enumeration<String> enum_ = cx.getLoggerNames();
1112            while (enum_.hasMoreElements()) {
1113                String name = enum_.nextElement();
1114                Logger logger = cx.findLogger(name);
1115                if (logger != null) {
1116                    resetLogger(logger);
1117                }
1118            }
1119        }
1120    }
1121
1122    // Private method to reset an individual target logger.
1123    private void resetLogger(Logger logger) {
1124        // Close all the Logger's handlers.
1125        Handler[] targets = logger.getHandlers();
1126        for (int i = 0; i < targets.length; i++) {
1127            Handler h = targets[i];
1128            logger.removeHandler(h);
1129            try {
1130                h.close();
1131            } catch (Exception ex) {
1132                // Problems closing a handler?  Keep going...
1133            }
1134        }
1135        String name = logger.getName();
1136        if (name != null && name.equals("")) {
1137            // This is the root logger.
1138            logger.setLevel(defaultLevel);
1139        } else {
1140            logger.setLevel(null);
1141        }
1142    }
1143
1144    // get a list of whitespace separated classnames from a property.
1145    private String[] parseClassNames(String propertyName) {
1146        String hands = getProperty(propertyName);
1147        if (hands == null) {
1148            return new String[0];
1149        }
1150        hands = hands.trim();
1151        int ix = 0;
1152        Vector<String> result = new Vector<>();
1153        while (ix < hands.length()) {
1154            int end = ix;
1155            while (end < hands.length()) {
1156                if (Character.isWhitespace(hands.charAt(end))) {
1157                    break;
1158                }
1159                if (hands.charAt(end) == ',') {
1160                    break;
1161                }
1162                end++;
1163            }
1164            String word = hands.substring(ix, end);
1165            ix = end+1;
1166            word = word.trim();
1167            if (word.length() == 0) {
1168                continue;
1169            }
1170            result.add(word);
1171        }
1172        return result.toArray(new String[result.size()]);
1173    }
1174
1175    /**
1176     * Reinitialize the logging properties and reread the logging configuration
1177     * from the given stream, which should be in java.util.Properties format.
1178     * A PropertyChangeEvent will be fired after the properties are read.
1179     * <p>
1180     * Any log level definitions in the new configuration file will be
1181     * applied using Logger.setLevel(), if the target Logger exists.
1182     *
1183     * @param ins       stream to read properties from
1184     * @exception  SecurityException  if a security manager exists and if
1185     *             the caller does not have LoggingPermission("control").
1186     * @exception  IOException if there are problems reading from the stream.
1187     */
1188    public void readConfiguration(InputStream ins) throws IOException, SecurityException {
1189        checkPermission();
1190        reset();
1191
1192        // Load the properties
1193        props.load(ins);
1194        // Instantiate new configuration objects.
1195        String names[] = parseClassNames("config");
1196
1197        for (int i = 0; i < names.length; i++) {
1198            String word = names[i];
1199            try {
1200                getClassInstance(word).newInstance();
1201            } catch (Exception ex) {
1202                System.err.println("Can't load config class \"" + word + "\"");
1203                System.err.println("" + ex);
1204                // ex.printStackTrace();
1205            }
1206        }
1207
1208        // Set levels on any pre-existing loggers, based on the new properties.
1209        setLevelsOnExistingLoggers();
1210
1211        // Notify any interested parties that our properties have changed.
1212        changes.firePropertyChange(null, null, null);
1213
1214        // Note that we need to reinitialize global handles when
1215        // they are first referenced.
1216        synchronized (this) {
1217            initializedGlobalHandlers = false;
1218        }
1219    }
1220
1221    /**
1222     * Get the value of a logging property.
1223     * The method returns null if the property is not found.
1224     * @param name      property name
1225     * @return          property value
1226     */
1227    public String getProperty(String name) {
1228        return props.getProperty(name);
1229    }
1230
1231    // Package private method to get a String property.
1232    // If the property is not defined we return the given
1233    // default value.
1234    String getStringProperty(String name, String defaultValue) {
1235        String val = getProperty(name);
1236        if (val == null) {
1237            return defaultValue;
1238        }
1239        return val.trim();
1240    }
1241
1242    // Package private method to get an integer property.
1243    // If the property is not defined or cannot be parsed
1244    // we return the given default value.
1245    int getIntProperty(String name, int defaultValue) {
1246        String val = getProperty(name);
1247        if (val == null) {
1248            return defaultValue;
1249        }
1250        try {
1251            return Integer.parseInt(val.trim());
1252        } catch (Exception ex) {
1253            return defaultValue;
1254        }
1255    }
1256
1257    // Package private method to get a boolean property.
1258    // If the property is not defined or cannot be parsed
1259    // we return the given default value.
1260    boolean getBooleanProperty(String name, boolean defaultValue) {
1261        String val = getProperty(name);
1262        if (val == null) {
1263            return defaultValue;
1264        }
1265        val = val.toLowerCase();
1266        if (val.equals("true") || val.equals("1")) {
1267            return true;
1268        } else if (val.equals("false") || val.equals("0")) {
1269            return false;
1270        }
1271        return defaultValue;
1272    }
1273
1274    // Package private method to get a Level property.
1275    // If the property is not defined or cannot be parsed
1276    // we return the given default value.
1277    Level getLevelProperty(String name, Level defaultValue) {
1278        String val = getProperty(name);
1279        if (val == null) {
1280            return defaultValue;
1281        }
1282        Level l = Level.findLevel(val.trim());
1283        return l != null ? l : defaultValue;
1284    }
1285
1286    // Package private method to get a filter property.
1287    // We return an instance of the class named by the "name"
1288    // property. If the property is not defined or has problems
1289    // we return the defaultValue.
1290    Filter getFilterProperty(String name, Filter defaultValue) {
1291        String val = getProperty(name);
1292        try {
1293            if (val != null) {
1294                return (Filter) getClassInstance(val).newInstance();
1295            }
1296        } catch (Exception ex) {
1297            // We got one of a variety of exceptions in creating the
1298            // class or creating an instance.
1299            // Drop through.
1300        }
1301        // We got an exception.  Return the defaultValue.
1302        return defaultValue;
1303    }
1304
1305
1306    // Package private method to get a formatter property.
1307    // We return an instance of the class named by the "name"
1308    // property. If the property is not defined or has problems
1309    // we return the defaultValue.
1310    Formatter getFormatterProperty(String name, Formatter defaultValue) {
1311        String val = getProperty(name);
1312        try {
1313            if (val != null) {
1314                return (Formatter) getClassInstance(val).newInstance();
1315            }
1316        } catch (Exception ex) {
1317            // We got one of a variety of exceptions in creating the
1318            // class or creating an instance.
1319            // Drop through.
1320        }
1321        // We got an exception.  Return the defaultValue.
1322        return defaultValue;
1323    }
1324
1325    // Private method to load the global handlers.
1326    // We do the real work lazily, when the global handlers
1327    // are first used.
1328    private synchronized void initializeGlobalHandlers() {
1329        if (initializedGlobalHandlers) {
1330            return;
1331        }
1332
1333        initializedGlobalHandlers = true;
1334
1335        if (deathImminent) {
1336            // Aaargh...
1337            // The VM is shutting down and our exit hook has been called.
1338            // Avoid allocating global handlers.
1339            return;
1340        }
1341        loadLoggerHandlers(rootLogger, null, "handlers");
1342    }
1343
1344    private final Permission controlPermission = new LoggingPermission("control", null);
1345
1346    void checkPermission() {
1347        SecurityManager sm = System.getSecurityManager();
1348        if (sm != null)
1349            sm.checkPermission(controlPermission);
1350    }
1351
1352    /**
1353     * Check that the current context is trusted to modify the logging
1354     * configuration.  This requires LoggingPermission("control").
1355     * <p>
1356     * If the check fails we throw a SecurityException, otherwise
1357     * we return normally.
1358     *
1359     * @exception  SecurityException  if a security manager exists and if
1360     *             the caller does not have LoggingPermission("control").
1361     */
1362    public void checkAccess() throws SecurityException {
1363        checkPermission();
1364    }
1365
1366    // Nested class to represent a node in our tree of named loggers.
1367    private static class LogNode {
1368        HashMap<String,LogNode> children;
1369        LoggerWeakRef loggerRef;
1370        LogNode parent;
1371        final LoggerContext context;
1372
1373        LogNode(LogNode parent, LoggerContext context) {
1374            this.parent = parent;
1375            this.context = context;
1376        }
1377
1378        // Recursive method to walk the tree below a node and set
1379        // a new parent logger.
1380        void walkAndSetParent(Logger parent) {
1381            if (children == null) {
1382                return;
1383            }
1384            Iterator<LogNode> values = children.values().iterator();
1385            while (values.hasNext()) {
1386                LogNode node = values.next();
1387                LoggerWeakRef ref = node.loggerRef;
1388                Logger logger = (ref == null) ? null : ref.get();
1389                if (logger == null) {
1390                    node.walkAndSetParent(parent);
1391                } else {
1392                    doSetParent(logger, parent);
1393                }
1394            }
1395        }
1396    }
1397
1398    // We use a subclass of Logger for the root logger, so
1399    // that we only instantiate the global handlers when they
1400    // are first needed.
1401    private class RootLogger extends Logger {
1402        private RootLogger() {
1403            super("", null);
1404            setLevel(defaultLevel);
1405        }
1406
1407        public void log(LogRecord record) {
1408            // Make sure that the global handlers have been instantiated.
1409            initializeGlobalHandlers();
1410            super.log(record);
1411        }
1412
1413        public void addHandler(Handler h) {
1414            initializeGlobalHandlers();
1415            super.addHandler(h);
1416        }
1417
1418        public void removeHandler(Handler h) {
1419            initializeGlobalHandlers();
1420            super.removeHandler(h);
1421        }
1422
1423        public Handler[] getHandlers() {
1424            initializeGlobalHandlers();
1425            return super.getHandlers();
1426        }
1427    }
1428
1429
1430    // Private method to be called when the configuration has
1431    // changed to apply any level settings to any pre-existing loggers.
1432    synchronized private void setLevelsOnExistingLoggers() {
1433        Enumeration<?> enum_ = props.propertyNames();
1434        while (enum_.hasMoreElements()) {
1435            String key = (String)enum_.nextElement();
1436            if (!key.endsWith(".level")) {
1437                // Not a level definition.
1438                continue;
1439            }
1440            int ix = key.length() - 6;
1441            String name = key.substring(0, ix);
1442            Level level = getLevelProperty(key, null);
1443            if (level == null) {
1444                System.err.println("Bad level value for property: " + key);
1445                continue;
1446            }
1447            for (LoggerContext cx : contexts()) {
1448                Logger l = cx.findLogger(name);
1449                if (l == null) {
1450                    continue;
1451                }
1452                l.setLevel(level);
1453            }
1454        }
1455    }
1456
1457    // Management Support
1458    private static LoggingMXBean loggingMXBean = null;
1459    /**
1460     * String representation of the {@code ObjectName} for the management interface
1461     * for the logging facility.
1462     *
1463     * @see java.util.logging.LoggingMXBean
1464     *
1465     * @since 1.5
1466     */
1467    // Android-changed : Remove reference to java.lang.management.ObjectName.
1468    //
1469    //@see java.lang.management.PlatformLoggingMXBean
1470    public final static String LOGGING_MXBEAN_NAME
1471        = "java.util.logging:type=Logging";
1472
1473    /**
1474     * Returns <tt>LoggingMXBean</tt> for managing loggers.
1475     *
1476     * @return a {@link LoggingMXBean} object.
1477     *
1478     * @since 1.5
1479     */
1480    // Android-removed docs areferring to java.lang.management.
1481    //
1482    // An alternative way to manage loggers is through the
1483    // {@link java.lang.management.PlatformLoggingMXBean} interface
1484    // that can be obtained by calling:
1485    // <pre>
1486    //     PlatformLoggingMXBean logging = {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class)
1487    //        ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class);
1488    // </pre>
1489    //
1490    // @see java.lang.management.PlatformLoggingMXBean
1491    public static synchronized LoggingMXBean getLoggingMXBean() {
1492        if (loggingMXBean == null) {
1493            loggingMXBean =  new Logging();
1494        }
1495        return loggingMXBean;
1496    }
1497
1498}
1499