1 /*
   2  * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 
  27 package java.util.logging;
  28 
  29 import java.io.*;
  30 import java.util.*;
  31 import java.security.*;
  32 import java.lang.ref.ReferenceQueue;
  33 import java.lang.ref.WeakReference;
  34 import java.beans.PropertyChangeListener;
  35 import java.beans.PropertyChangeEvent;
  36 import java.net.URL;
  37 import sun.security.action.GetPropertyAction;
  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 
 148 public class LogManager {
 149     // The global LogManager object
 150     private static LogManager manager;
 151 
 152     private final static Handler[] emptyHandlers = { };
 153     private Properties props = new Properties();
 154     private final static Level defaultLevel = Level.INFO;
 155 
 156     // The map of the registered listeners. The map value is the registration
 157     // count to allow for cases where the same listener is registered many times.
 158     private final Map<PropertyChangeListener,Integer> listenerMap = new HashMap<>();
 159 
 160     // Table of named Loggers that maps names to Loggers.
 161     private Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();
 162     // Tree of named Loggers
 163     private LogNode root = new LogNode(null);
 164     private Logger rootLogger;
 165 
 166     // Have we done the primordial reading of the configuration file?
 167     // (Must be done after a suitable amount of java.lang.System
 168     // initialization has been done)
 169     private volatile boolean readPrimordialConfiguration;
 170     // Have we initialized global (root) handlers yet?
 171     // This gets set to false in readConfiguration
 172     private boolean initializedGlobalHandlers = true;
 173     // True if JVM death is imminent and the exit hook has been called.
 174     private boolean deathImminent;
 175 
 176     static {
 177         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 178                 public Object run() {
 179                     String cname = null;
 180                     try {
 181                         cname = System.getProperty("java.util.logging.manager");
 182                         if (cname != null) {
 183                             try {
 184                                 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
 185                                 manager = (LogManager) clz.newInstance();
 186                             } catch (ClassNotFoundException ex) {
 187                                 Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
 188                                 manager = (LogManager) clz.newInstance();
 189                             }
 190                         }
 191                     } catch (Exception ex) {
 192                         System.err.println("Could not load Logmanager \"" + cname + "\"");
 193                         ex.printStackTrace();
 194                     }
 195                     if (manager == null) {
 196                         manager = new LogManager();
 197                     }
 198 
 199                     // Create and retain Logger for the root of the namespace.
 200                     manager.rootLogger = manager.new RootLogger();
 201                     manager.addLogger(manager.rootLogger);
 202 
 203                     // Adding the global Logger. Doing so in the Logger.<clinit>
 204                     // would deadlock with the LogManager.<clinit>.
 205                     Logger.getGlobal().setLogManager(manager);
 206                     manager.addLogger(Logger.getGlobal());
 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                     try {
 285                         AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
 286                                 public Object run() throws Exception {
 287                                     readConfiguration();
 288 
 289                                     // Platform loggers begin to delegate to java.util.logging.Logger
 290                                     sun.util.logging.PlatformLogger.redirectPlatformLoggers();
 291 
 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      * @deprecated The dependency on {@code PropertyChangeListener} creates a
 315      *             significant impediment to future modularization of the Java
 316      *             platform. This method will be removed in a future release.
 317      *             The global {@code LogManager} can detect changes to the
 318      *             logging configuration by overridding the {@link
 319      *             #readConfiguration readConfiguration} method.
 320      */
 321     @Deprecated
 322     public void addPropertyChangeListener(PropertyChangeListener l) throws SecurityException {
 323         PropertyChangeListener listener = Objects.requireNonNull(l);
 324         checkPermission();
 325         synchronized (listenerMap) {
 326             // increment the registration count if already registered
 327             Integer value = listenerMap.get(listener);
 328             value = (value == null) ? 1 : (value + 1);
 329             listenerMap.put(listener, value);
 330         }
 331     }
 332 
 333     /**
 334      * Removes an event listener for property change events.
 335      * If the same listener instance has been added to the listener table
 336      * through multiple invocations of <CODE>addPropertyChangeListener</CODE>,
 337      * then an equivalent number of
 338      * <CODE>removePropertyChangeListener</CODE> invocations are required to remove
 339      * all instances of that listener from the listener table.
 340      * <P>
 341      * Returns silently if the given listener is not found.
 342      *
 343      * @param l  event listener (can be null)
 344      * @exception  SecurityException  if a security manager exists and if
 345      *             the caller does not have LoggingPermission("control").
 346      * @deprecated The dependency on {@code PropertyChangeListener} creates a
 347      *             significant impediment to future modularization of the Java
 348      *             platform. This method will be removed in a future release.
 349      *             The global {@code LogManager} can detect changes to the
 350      *             logging configuration by overridding the {@link
 351      *             #readConfiguration readConfiguration} method.
 352      */
 353     @Deprecated
 354     public void removePropertyChangeListener(PropertyChangeListener l) throws SecurityException {
 355         checkPermission();
 356         if (l != null) {
 357             PropertyChangeListener listener = l;
 358             synchronized (listenerMap) {
 359                 Integer value = listenerMap.get(listener);
 360                 if (value != null) {
 361                     // remove from map if registration count is 1, otherwise
 362                     // just decrement its count
 363                     int i = value.intValue();
 364                     if (i == 1) {
 365                         listenerMap.remove(listener);
 366                     } else {
 367                         assert i > 1;
 368                         listenerMap.put(listener, i - 1);
 369                     }
 370                 }
 371             }
 372         }
 373     }
 374 
 375     // Package-level method.
 376     // Find or create a specified logger instance. If a logger has
 377     // already been created with the given name it is returned.
 378     // Otherwise a new logger instance is created and registered
 379     // in the LogManager global namespace.
 380 
 381     // This method will always return a non-null Logger object.
 382     // Synchronization is not required here. All synchronization for
 383     // adding a new Logger object is handled by addLogger().
 384     Logger demandLogger(String name) {
 385         Logger result = getLogger(name);
 386         if (result == null) {
 387             // only allocate the new logger once
 388             Logger newLogger = new Logger(name, null);
 389             do {
 390                 if (addLogger(newLogger)) {
 391                     // We successfully added the new Logger that we
 392                     // created above so return it without refetching.
 393                     return newLogger;
 394                 }
 395 
 396                 // We didn't add the new Logger that we created above
 397                 // because another thread added a Logger with the same
 398                 // name after our null check above and before our call
 399                 // to addLogger(). We have to refetch the Logger because
 400                 // addLogger() returns a boolean instead of the Logger
 401                 // reference itself. However, if the thread that created
 402                 // the other Logger is not holding a strong reference to
 403                 // the other Logger, then it is possible for the other
 404                 // Logger to be GC'ed after we saw it in addLogger() and
 405                 // before we can refetch it. If it has been GC'ed then
 406                 // we'll just loop around and try again.
 407                 result = getLogger(name);
 408             } while (result == null);
 409         }
 410         return result;
 411     }
 412 
 413     // If logger.getUseParentHandlers() returns 'true' and any of the logger's
 414     // parents have levels or handlers defined, make sure they are instantiated.
 415     private void processParentHandlers(Logger logger, String name) {
 416         int ix = 1;
 417         for (;;) {
 418             int ix2 = name.indexOf(".", ix);
 419             if (ix2 < 0) {
 420                 break;
 421             }
 422             String pname = name.substring(0,ix2);
 423 
 424             if (getProperty(pname+".level")    != null ||
 425                 getProperty(pname+".handlers") != null) {
 426                 // This pname has a level/handlers definition.
 427                 // Make sure it exists.
 428                 demandLogger(pname);
 429             }
 430             ix = ix2+1;
 431         }
 432     }
 433 
 434     // Add new per logger handlers.
 435     // We need to raise privilege here. All our decisions will
 436     // be made based on the logging configuration, which can
 437     // only be modified by trusted code.
 438     private void loadLoggerHandlers(final Logger logger, final String name,
 439                                     final String handlersPropertyName) {
 440         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 441             public Object run() {
 442                 if (logger != rootLogger) {
 443                     boolean useParent = getBooleanProperty(name + ".useParentHandlers", true);
 444                     if (!useParent) {
 445                         logger.setUseParentHandlers(false);
 446                     }
 447                 }
 448 
 449                 String names[] = parseClassNames(handlersPropertyName);
 450                 for (int i = 0; i < names.length; i++) {
 451                     String word = names[i];
 452                     try {
 453                         Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word);
 454                         Handler  hdl = (Handler) clz.newInstance();
 455                         try {
 456                             // Check if there is a property defining the
 457                             // this handler's level.
 458                             String levs = getProperty(word + ".level");
 459                             if (levs != null) {
 460                                 hdl.setLevel(Level.parse(levs));
 461                             }
 462                         } catch (Exception ex) {
 463                             System.err.println("Can't set level for " + word);
 464                             // Probably a bad level. Drop through.
 465                         }
 466                         // Add this Handler to the logger
 467                         logger.addHandler(hdl);
 468                     } catch (Exception ex) {
 469                         System.err.println("Can't load log handler \"" + word + "\"");
 470                         System.err.println("" + ex);
 471                         ex.printStackTrace();
 472                     }
 473                 }
 474                 return null;
 475             }});
 476     }
 477 
 478 
 479     // loggerRefQueue holds LoggerWeakRef objects for Logger objects
 480     // that have been GC'ed.
 481     private final ReferenceQueue<Logger> loggerRefQueue
 482         = new ReferenceQueue<>();
 483 
 484     // Package-level inner class.
 485     // Helper class for managing WeakReferences to Logger objects.
 486     //
 487     // LogManager.namedLoggers
 488     //     - has weak references to all named Loggers
 489     //     - namedLoggers keeps the LoggerWeakRef objects for the named
 490     //       Loggers around until we can deal with the book keeping for
 491     //       the named Logger that is being GC'ed.
 492     // LogManager.LogNode.loggerRef
 493     //     - has a weak reference to a named Logger
 494     //     - the LogNode will also keep the LoggerWeakRef objects for
 495     //       the named Loggers around; currently LogNodes never go away.
 496     // Logger.kids
 497     //     - has a weak reference to each direct child Logger; this
 498     //       includes anonymous and named Loggers
 499     //     - anonymous Loggers are always children of the rootLogger
 500     //       which is a strong reference; rootLogger.kids keeps the
 501     //       LoggerWeakRef objects for the anonymous Loggers around
 502     //       until we can deal with the book keeping.
 503     //
 504     final class LoggerWeakRef extends WeakReference<Logger> {
 505         private String                name;       // for namedLoggers cleanup
 506         private LogNode               node;       // for loggerRef cleanup
 507         private WeakReference<Logger> parentRef;  // for kids cleanup
 508 
 509         LoggerWeakRef(Logger logger) {
 510             super(logger, loggerRefQueue);
 511 
 512             name = logger.getName();  // save for namedLoggers cleanup
 513         }
 514 
 515         // dispose of this LoggerWeakRef object
 516         void dispose() {
 517             if (node != null) {
 518                 // if we have a LogNode, then we were a named Logger
 519                 // so clear namedLoggers weak ref to us
 520                 manager.namedLoggers.remove(name);
 521                 name = null;  // clear our ref to the Logger's name
 522 
 523                 node.loggerRef = null;  // clear LogNode's weak ref to us
 524                 node = null;            // clear our ref to LogNode
 525             }
 526 
 527             if (parentRef != null) {
 528                 // this LoggerWeakRef has or had a parent Logger
 529                 Logger parent = parentRef.get();
 530                 if (parent != null) {
 531                     // the parent Logger is still there so clear the
 532                     // parent Logger's weak ref to us
 533                     parent.removeChildLogger(this);
 534                 }
 535                 parentRef = null;  // clear our weak ref to the parent Logger
 536             }
 537         }
 538 
 539         // set the node field to the specified value
 540         void setNode(LogNode node) {
 541             this.node = node;
 542         }
 543 
 544         // set the parentRef field to the specified value
 545         void setParentRef(WeakReference<Logger> parentRef) {
 546             this.parentRef = parentRef;
 547         }
 548     }
 549 
 550     // Package-level method.
 551     // Drain some Logger objects that have been GC'ed.
 552     //
 553     // drainLoggerRefQueueBounded() is called by addLogger() below
 554     // and by Logger.getAnonymousLogger(String) so we'll drain up to
 555     // MAX_ITERATIONS GC'ed Loggers for every Logger we add.
 556     //
 557     // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives
 558     // us about a 50/50 mix in increased weak ref counts versus
 559     // decreased weak ref counts in the AnonLoggerWeakRefLeak test.
 560     // Here are stats for cleaning up sets of 400 anonymous Loggers:
 561     //   - test duration 1 minute
 562     //   - sample size of 125 sets of 400
 563     //   - average: 1.99 ms
 564     //   - minimum: 0.57 ms
 565     //   - maximum: 25.3 ms
 566     //
 567     // The same config gives us a better decreased weak ref count
 568     // than increased weak ref count in the LoggerWeakRefLeak test.
 569     // Here are stats for cleaning up sets of 400 named Loggers:
 570     //   - test duration 2 minutes
 571     //   - sample size of 506 sets of 400
 572     //   - average: 0.57 ms
 573     //   - minimum: 0.02 ms
 574     //   - maximum: 10.9 ms
 575     //
 576     private final static int MAX_ITERATIONS = 400;
 577     final synchronized void drainLoggerRefQueueBounded() {
 578         for (int i = 0; i < MAX_ITERATIONS; i++) {
 579             if (loggerRefQueue == null) {
 580                 // haven't finished loading LogManager yet
 581                 break;
 582             }
 583 
 584             LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll();
 585             if (ref == null) {
 586                 break;
 587             }
 588             // a Logger object has been GC'ed so clean it up
 589             ref.dispose();
 590         }
 591     }
 592 
 593     /**
 594      * Add a named logger.  This does nothing and returns false if a logger
 595      * with the same name is already registered.
 596      * <p>
 597      * The Logger factory methods call this method to register each
 598      * newly created Logger.
 599      * <p>
 600      * The application should retain its own reference to the Logger
 601      * object to avoid it being garbage collected.  The LogManager
 602      * may only retain a weak reference.
 603      *
 604      * @param   logger the new logger.
 605      * @return  true if the argument logger was registered successfully,
 606      *          false if a logger of that name already exists.
 607      * @exception NullPointerException if the logger name is null.
 608      */
 609     public synchronized boolean addLogger(Logger logger) {
 610         final String name = logger.getName();
 611         if (name == null) {
 612             throw new NullPointerException();
 613         }
 614 
 615         // cleanup some Loggers that have been GC'ed
 616         drainLoggerRefQueueBounded();
 617 
 618         LoggerWeakRef ref = namedLoggers.get(name);
 619         if (ref != null) {
 620             if (ref.get() == null) {
 621                 // It's possible that the Logger was GC'ed after the
 622                 // drainLoggerRefQueueBounded() call above so allow
 623                 // a new one to be registered.
 624                 namedLoggers.remove(name);
 625             } else {
 626                 // We already have a registered logger with the given name.
 627                 return false;
 628             }
 629         }
 630 
 631         // We're adding a new logger.
 632         // Note that we are creating a weak reference here.
 633         ref = new LoggerWeakRef(logger);
 634         namedLoggers.put(name, ref);
 635 
 636         // Apply any initial level defined for the new logger.
 637         Level level = getLevelProperty(name+".level", null);
 638         if (level != null) {
 639             doSetLevel(logger, level);
 640         }
 641 
 642         // Do we have a per logger handler too?
 643         // Note: this will add a 200ms penalty
 644         loadLoggerHandlers(logger, name, name+".handlers");
 645         processParentHandlers(logger, name);
 646 
 647         // Find the new node and its parent.
 648         LogNode node = findNode(name);
 649         node.loggerRef = ref;
 650         Logger parent = null;
 651         LogNode nodep = node.parent;
 652         while (nodep != null) {
 653             LoggerWeakRef nodeRef = nodep.loggerRef;
 654             if (nodeRef != null) {
 655                 parent = nodeRef.get();
 656                 if (parent != null) {
 657                     break;
 658                 }
 659             }
 660             nodep = nodep.parent;
 661         }
 662 
 663         if (parent != null) {
 664             doSetParent(logger, parent);
 665         }
 666         // Walk over the children and tell them we are their new parent.
 667         node.walkAndSetParent(logger);
 668 
 669         // new LogNode is ready so tell the LoggerWeakRef about it
 670         ref.setNode(node);
 671 
 672         return true;
 673     }
 674 
 675 
 676     // Private method to set a level on a logger.
 677     // If necessary, we raise privilege before doing the call.
 678     private static void doSetLevel(final Logger logger, final Level level) {
 679         SecurityManager sm = System.getSecurityManager();
 680         if (sm == null) {
 681             // There is no security manager, so things are easy.
 682             logger.setLevel(level);
 683             return;
 684         }
 685         // There is a security manager.  Raise privilege before
 686         // calling setLevel.
 687         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 688             public Object run() {
 689                 logger.setLevel(level);
 690                 return null;
 691             }});
 692     }
 693 
 694 
 695 
 696     // Private method to set a parent on a logger.
 697     // If necessary, we raise privilege before doing the setParent call.
 698     private static void doSetParent(final Logger logger, final Logger parent) {
 699         SecurityManager sm = System.getSecurityManager();
 700         if (sm == null) {
 701             // There is no security manager, so things are easy.
 702             logger.setParent(parent);
 703             return;
 704         }
 705         // There is a security manager.  Raise privilege before
 706         // calling setParent.
 707         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 708             public Object run() {
 709                 logger.setParent(parent);
 710                 return null;
 711             }});
 712     }
 713 
 714     // Find a node in our tree of logger nodes.
 715     // If necessary, create it.
 716     private LogNode findNode(String name) {
 717         if (name == null || name.equals("")) {
 718             return root;
 719         }
 720         LogNode node = root;
 721         while (name.length() > 0) {
 722             int ix = name.indexOf(".");
 723             String head;
 724             if (ix > 0) {
 725                 head = name.substring(0,ix);
 726                 name = name.substring(ix+1);
 727             } else {
 728                 head = name;
 729                 name = "";
 730             }
 731             if (node.children == null) {
 732                 node.children = new HashMap<>();
 733             }
 734             LogNode child = node.children.get(head);
 735             if (child == null) {
 736                 child = new LogNode(node);
 737                 node.children.put(head, child);
 738             }
 739             node = child;
 740         }
 741         return node;
 742     }
 743 
 744     /**
 745      * Method to find a named logger.
 746      * <p>
 747      * Note that since untrusted code may create loggers with
 748      * arbitrary names this method should not be relied on to
 749      * find Loggers for security sensitive logging.
 750      * It is also important to note that the Logger associated with the
 751      * String {@code name} may be garbage collected at any time if there
 752      * is no strong reference to the Logger. The caller of this method
 753      * must check the return value for null in order to properly handle
 754      * the case where the Logger has been garbage collected.
 755      * <p>
 756      * @param name name of the logger
 757      * @return  matching logger or null if none is found
 758      */
 759     public synchronized Logger getLogger(String name) {
 760         LoggerWeakRef ref = namedLoggers.get(name);
 761         if (ref == null) {
 762             return null;
 763         }
 764         Logger logger = ref.get();
 765         if (logger == null) {
 766             // Hashtable holds stale weak reference
 767             // to a logger which has been GC-ed.
 768             namedLoggers.remove(name);
 769         }
 770         return logger;
 771     }
 772 
 773     /**
 774      * Get an enumeration of known logger names.
 775      * <p>
 776      * Note:  Loggers may be added dynamically as new classes are loaded.
 777      * This method only reports on the loggers that are currently registered.
 778      * It is also important to note that this method only returns the name
 779      * of a Logger, not a strong reference to the Logger itself.
 780      * The returned String does nothing to prevent the Logger from being
 781      * garbage collected. In particular, if the returned name is passed
 782      * to {@code LogManager.getLogger()}, then the caller must check the
 783      * return value from {@code LogManager.getLogger()} for null to properly
 784      * handle the case where the Logger has been garbage collected in the
 785      * time since its name was returned by this method.
 786      * <p>
 787      * @return  enumeration of logger name strings
 788      */
 789     public synchronized Enumeration<String> getLoggerNames() {
 790         return namedLoggers.keys();
 791     }
 792 
 793     /**
 794      * Reinitialize the logging properties and reread the logging configuration.
 795      * <p>
 796      * The same rules are used for locating the configuration properties
 797      * as are used at startup.  So normally the logging properties will
 798      * be re-read from the same file that was used at startup.
 799      * <P>
 800      * Any log level definitions in the new configuration file will be
 801      * applied using Logger.setLevel(), if the target Logger exists.
 802      * <p>
 803      * A PropertyChangeEvent will be fired after the properties are read.
 804      *
 805      * @exception  SecurityException  if a security manager exists and if
 806      *             the caller does not have LoggingPermission("control").
 807      * @exception  IOException if there are IO problems reading the configuration.
 808      */
 809     public void readConfiguration() throws IOException, SecurityException {
 810         checkPermission();
 811 
 812         // if a configuration class is specified, load it and use it.
 813         String cname = System.getProperty("java.util.logging.config.class");
 814         if (cname != null) {
 815             try {
 816                 // Instantiate the named class.  It is its constructor's
 817                 // responsibility to initialize the logging configuration, by
 818                 // calling readConfiguration(InputStream) with a suitable stream.
 819                 try {
 820                     Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
 821                     clz.newInstance();
 822                     return;
 823                 } catch (ClassNotFoundException ex) {
 824                     Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
 825                     clz.newInstance();
 826                     return;
 827                 }
 828             } catch (Exception ex) {
 829                 System.err.println("Logging configuration class \"" + cname + "\" failed");
 830                 System.err.println("" + ex);
 831                 // keep going and useful config file.
 832             }
 833         }
 834 
 835         String fname = System.getProperty("java.util.logging.config.file");
 836         if (fname == null) {
 837             fname = System.getProperty("java.home");
 838             if (fname == null) {
 839                 throw new Error("Can't find java.home ??");
 840             }
 841             File f = new File(fname, "lib");
 842             f = new File(f, "logging.properties");
 843             fname = f.getCanonicalPath();
 844         }
 845         InputStream in = new FileInputStream(fname);
 846         BufferedInputStream bin = new BufferedInputStream(in);
 847         try {
 848             readConfiguration(bin);
 849         } finally {
 850             if (in != null) {
 851                 in.close();
 852             }
 853         }
 854     }
 855 
 856     /**
 857      * Reset the logging configuration.
 858      * <p>
 859      * For all named loggers, the reset operation removes and closes
 860      * all Handlers and (except for the root logger) sets the level
 861      * to null.  The root logger's level is set to Level.INFO.
 862      *
 863      * @exception  SecurityException  if a security manager exists and if
 864      *             the caller does not have LoggingPermission("control").
 865      */
 866 
 867     public void reset() throws SecurityException {
 868         checkPermission();
 869         synchronized (this) {
 870             props = new Properties();
 871             // Since we are doing a reset we no longer want to initialize
 872             // the global handlers, if they haven't been initialized yet.
 873             initializedGlobalHandlers = true;
 874         }
 875         Enumeration<String> enum_ = getLoggerNames();
 876         while (enum_.hasMoreElements()) {
 877             String name = enum_.nextElement();
 878             resetLogger(name);
 879         }
 880     }
 881 
 882 
 883     // Private method to reset an individual target logger.
 884     private void resetLogger(String name) {
 885         Logger logger = getLogger(name);
 886         if (logger == null) {
 887             return;
 888         }
 889         // Close all the Logger's handlers.
 890         Handler[] targets = logger.getHandlers();
 891         for (int i = 0; i < targets.length; i++) {
 892             Handler h = targets[i];
 893             logger.removeHandler(h);
 894             try {
 895                 h.close();
 896             } catch (Exception ex) {
 897                 // Problems closing a handler?  Keep going...
 898             }
 899         }
 900         if (name != null && name.equals("")) {
 901             // This is the root logger.
 902             logger.setLevel(defaultLevel);
 903         } else {
 904             logger.setLevel(null);
 905         }
 906     }
 907 
 908     // get a list of whitespace separated classnames from a property.
 909     private String[] parseClassNames(String propertyName) {
 910         String hands = getProperty(propertyName);
 911         if (hands == null) {
 912             return new String[0];
 913         }
 914         hands = hands.trim();
 915         int ix = 0;
 916         Vector<String> result = new Vector<>();
 917         while (ix < hands.length()) {
 918             int end = ix;
 919             while (end < hands.length()) {
 920                 if (Character.isWhitespace(hands.charAt(end))) {
 921                     break;
 922                 }
 923                 if (hands.charAt(end) == ',') {
 924                     break;
 925                 }
 926                 end++;
 927             }
 928             String word = hands.substring(ix, end);
 929             ix = end+1;
 930             word = word.trim();
 931             if (word.length() == 0) {
 932                 continue;
 933             }
 934             result.add(word);
 935         }
 936         return result.toArray(new String[result.size()]);
 937     }
 938 
 939     /**
 940      * Reinitialize the logging properties and reread the logging configuration
 941      * from the given stream, which should be in java.util.Properties format.
 942      * A PropertyChangeEvent will be fired after the properties are read.
 943      * <p>
 944      * Any log level definitions in the new configuration file will be
 945      * applied using Logger.setLevel(), if the target Logger exists.
 946      *
 947      * @param ins       stream to read properties from
 948      * @exception  SecurityException  if a security manager exists and if
 949      *             the caller does not have LoggingPermission("control").
 950      * @exception  IOException if there are problems reading from the stream.
 951      */
 952     public void readConfiguration(InputStream ins) throws IOException, SecurityException {
 953         checkPermission();
 954         reset();
 955 
 956         // Load the properties
 957         props.load(ins);
 958         // Instantiate new configuration objects.
 959         String names[] = parseClassNames("config");
 960 
 961         for (int i = 0; i < names.length; i++) {
 962             String word = names[i];
 963             try {
 964                 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word);
 965                 clz.newInstance();
 966             } catch (Exception ex) {
 967                 System.err.println("Can't load config class \"" + word + "\"");
 968                 System.err.println("" + ex);
 969                 // ex.printStackTrace();
 970             }
 971         }
 972 
 973         // Set levels on any pre-existing loggers, based on the new properties.
 974         setLevelsOnExistingLoggers();
 975 
 976         // Notify any interested parties that our properties have changed.
 977         // We first take a copy of the listener map so that we aren't holding any
 978         // locks when calling the listeners.
 979         Map<PropertyChangeListener,Integer> listeners = null;
 980         synchronized (listenerMap) {
 981             if (!listenerMap.isEmpty())
 982                 listeners = new HashMap<>(listenerMap);
 983         }
 984         if (listeners != null) {
 985             PropertyChangeEvent ev = new PropertyChangeEvent(LogManager.class, null, null, null);
 986             for (Map.Entry<PropertyChangeListener,Integer> entry : listeners.entrySet()) {
 987                 PropertyChangeListener listener = entry.getKey();
 988                 int count = entry.getValue().intValue();
 989                 for (int i = 0; i < count; i++) {
 990                     listener.propertyChange(ev);
 991                 }
 992             }
 993         }
 994 
 995         // Note that we need to reinitialize global handles when
 996         // they are first referenced.
 997         synchronized (this) {
 998             initializedGlobalHandlers = false;
 999         }
1000     }
1001 
1002     /**
1003      * Get the value of a logging property.
1004      * The method returns null if the property is not found.
1005      * @param name      property name
1006      * @return          property value
1007      */
1008     public String getProperty(String name) {
1009         return props.getProperty(name);
1010     }
1011 
1012     // Package private method to get a String property.
1013     // If the property is not defined we return the given
1014     // default value.
1015     String getStringProperty(String name, String defaultValue) {
1016         String val = getProperty(name);
1017         if (val == null) {
1018             return defaultValue;
1019         }
1020         return val.trim();
1021     }
1022 
1023     // Package private method to get an integer property.
1024     // If the property is not defined or cannot be parsed
1025     // we return the given default value.
1026     int getIntProperty(String name, int defaultValue) {
1027         String val = getProperty(name);
1028         if (val == null) {
1029             return defaultValue;
1030         }
1031         try {
1032             return Integer.parseInt(val.trim());
1033         } catch (Exception ex) {
1034             return defaultValue;
1035         }
1036     }
1037 
1038     // Package private method to get a boolean property.
1039     // If the property is not defined or cannot be parsed
1040     // we return the given default value.
1041     boolean getBooleanProperty(String name, boolean defaultValue) {
1042         String val = getProperty(name);
1043         if (val == null) {
1044             return defaultValue;
1045         }
1046         val = val.toLowerCase();
1047         if (val.equals("true") || val.equals("1")) {
1048             return true;
1049         } else if (val.equals("false") || val.equals("0")) {
1050             return false;
1051         }
1052         return defaultValue;
1053     }
1054 
1055     // Package private method to get a Level property.
1056     // If the property is not defined or cannot be parsed
1057     // we return the given default value.
1058     Level getLevelProperty(String name, Level defaultValue) {
1059         String val = getProperty(name);
1060         if (val == null) {
1061             return defaultValue;
1062         }
1063         try {
1064             return Level.parse(val.trim());
1065         } catch (Exception ex) {
1066             return defaultValue;
1067         }
1068     }
1069 
1070     // Package private method to get a filter property.
1071     // We return an instance of the class named by the "name"
1072     // property. If the property is not defined or has problems
1073     // we return the defaultValue.
1074     Filter getFilterProperty(String name, Filter defaultValue) {
1075         String val = getProperty(name);
1076         try {
1077             if (val != null) {
1078                 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val);
1079                 return (Filter) clz.newInstance();
1080             }
1081         } catch (Exception ex) {
1082             // We got one of a variety of exceptions in creating the
1083             // class or creating an instance.
1084             // Drop through.
1085         }
1086         // We got an exception.  Return the defaultValue.
1087         return defaultValue;
1088     }
1089 
1090 
1091     // Package private method to get a formatter property.
1092     // We return an instance of the class named by the "name"
1093     // property. If the property is not defined or has problems
1094     // we return the defaultValue.
1095     Formatter getFormatterProperty(String name, Formatter defaultValue) {
1096         String val = getProperty(name);
1097         try {
1098             if (val != null) {
1099                 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val);
1100                 return (Formatter) clz.newInstance();
1101             }
1102         } catch (Exception ex) {
1103             // We got one of a variety of exceptions in creating the
1104             // class or creating an instance.
1105             // Drop through.
1106         }
1107         // We got an exception.  Return the defaultValue.
1108         return defaultValue;
1109     }
1110 
1111     // Private method to load the global handlers.
1112     // We do the real work lazily, when the global handlers
1113     // are first used.
1114     private synchronized void initializeGlobalHandlers() {
1115         if (initializedGlobalHandlers) {
1116             return;
1117         }
1118 
1119         initializedGlobalHandlers = true;
1120 
1121         if (deathImminent) {
1122             // Aaargh...
1123             // The VM is shutting down and our exit hook has been called.
1124             // Avoid allocating global handlers.
1125             return;
1126         }
1127         loadLoggerHandlers(rootLogger, null, "handlers");
1128     }
1129 
1130     private final Permission controlPermission = new LoggingPermission("control", null);
1131 
1132     void checkPermission() {
1133         SecurityManager sm = System.getSecurityManager();
1134         if (sm != null)
1135             sm.checkPermission(controlPermission);
1136     }
1137 
1138     /**
1139      * Check that the current context is trusted to modify the logging
1140      * configuration.  This requires LoggingPermission("control").
1141      * <p>
1142      * If the check fails we throw a SecurityException, otherwise
1143      * we return normally.
1144      *
1145      * @exception  SecurityException  if a security manager exists and if
1146      *             the caller does not have LoggingPermission("control").
1147      */
1148     public void checkAccess() throws SecurityException {
1149         checkPermission();
1150     }
1151 
1152     // Nested class to represent a node in our tree of named loggers.
1153     private static class LogNode {
1154         HashMap<String,LogNode> children;
1155         LoggerWeakRef loggerRef;
1156         LogNode parent;
1157 
1158         LogNode(LogNode parent) {
1159             this.parent = parent;
1160         }
1161 
1162         // Recursive method to walk the tree below a node and set
1163         // a new parent logger.
1164         void walkAndSetParent(Logger parent) {
1165             if (children == null) {
1166                 return;
1167             }
1168             Iterator<LogNode> values = children.values().iterator();
1169             while (values.hasNext()) {
1170                 LogNode node = values.next();
1171                 LoggerWeakRef ref = node.loggerRef;
1172                 Logger logger = (ref == null) ? null : ref.get();
1173                 if (logger == null) {
1174                     node.walkAndSetParent(parent);
1175                 } else {
1176                     doSetParent(logger, parent);
1177                 }
1178             }
1179         }
1180     }
1181 
1182     // We use a subclass of Logger for the root logger, so
1183     // that we only instantiate the global handlers when they
1184     // are first needed.
1185     private class RootLogger extends Logger {
1186 
1187         private RootLogger() {
1188             super("", null);
1189             setLevel(defaultLevel);
1190         }
1191 
1192         public void log(LogRecord record) {
1193             // Make sure that the global handlers have been instantiated.
1194             initializeGlobalHandlers();
1195             super.log(record);
1196         }
1197 
1198         public void addHandler(Handler h) {
1199             initializeGlobalHandlers();
1200             super.addHandler(h);
1201         }
1202 
1203         public void removeHandler(Handler h) {
1204             initializeGlobalHandlers();
1205             super.removeHandler(h);
1206         }
1207 
1208         public Handler[] getHandlers() {
1209             initializeGlobalHandlers();
1210             return super.getHandlers();
1211         }
1212     }
1213 
1214 
1215     // Private method to be called when the configuration has
1216     // changed to apply any level settings to any pre-existing loggers.
1217     synchronized private void setLevelsOnExistingLoggers() {
1218         Enumeration<?> enum_ = props.propertyNames();
1219         while (enum_.hasMoreElements()) {
1220             String key = (String)enum_.nextElement();
1221             if (!key.endsWith(".level")) {
1222                 // Not a level definition.
1223                 continue;
1224             }
1225             int ix = key.length() - 6;
1226             String name = key.substring(0, ix);
1227             Level level = getLevelProperty(key, null);
1228             if (level == null) {
1229                 System.err.println("Bad level value for property: " + key);
1230                 continue;
1231             }
1232             Logger l = getLogger(name);
1233             if (l == null) {
1234                 continue;
1235             }
1236             l.setLevel(level);
1237         }
1238     }
1239 
1240     // Management Support
1241     private static LoggingMXBean loggingMXBean = null;
1242     /**
1243      * String representation of the
1244      * {@link javax.management.ObjectName} for the management interface
1245      * for the logging facility.
1246      *
1247      * @see java.lang.management.PlatformLoggingMXBean
1248      * @see java.util.logging.LoggingMXBean
1249      *
1250      * @since 1.5
1251      */
1252     public final static String LOGGING_MXBEAN_NAME
1253         = "java.util.logging:type=Logging";
1254 
1255     /**
1256      * Returns <tt>LoggingMXBean</tt> for managing loggers.
1257      * An alternative way to manage loggers is through the
1258      * {@link java.lang.management.PlatformLoggingMXBean} interface
1259      * that can be obtained by calling:
1260      * <pre>
1261      *     PlatformLoggingMXBean logging = {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class)
1262      *         ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class);
1263      * </pre>
1264      *
1265      * @return a {@link LoggingMXBean} object.
1266      *
1267      * @see java.lang.management.PlatformLoggingMXBean
1268      * @since 1.5
1269      */
1270     public static synchronized LoggingMXBean getLoggingMXBean() {
1271         if (loggingMXBean == null) {
1272             loggingMXBean =  new Logging();
1273         }
1274         return loggingMXBean;
1275     }
1276 
1277 }