1 /*
   2  * Copyright (c) 2000, 2010, 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.util.*;
  30 import java.util.concurrent.CopyOnWriteArrayList;
  31 import java.security.*;
  32 import java.lang.ref.WeakReference;
  33 
  34 /**
  35  * A Logger object is used to log messages for a specific
  36  * system or application component.  Loggers are normally named,
  37  * using a hierarchical dot-separated namespace.  Logger names
  38  * can be arbitrary strings, but they should normally be based on
  39  * the package name or class name of the logged component, such
  40  * as java.net or javax.swing.  In addition it is possible to create
  41  * "anonymous" Loggers that are not stored in the Logger namespace.
  42  * <p>
  43  * Logger objects may be obtained by calls on one of the getLogger
  44  * factory methods.  These will either create a new Logger or
  45  * return a suitable existing Logger. It is important to note that
  46  * the Logger returned by one of the {@code getLogger} factory methods
  47  * may be garbage collected at any time if a strong reference to the
  48  * Logger is not kept.
  49  * <p>
  50  * Logging messages will be forwarded to registered Handler
  51  * objects, which can forward the messages to a variety of
  52  * destinations, including consoles, files, OS logs, etc.
  53  * <p>
  54  * Each Logger keeps track of a "parent" Logger, which is its
  55  * nearest existing ancestor in the Logger namespace.
  56  * <p>
  57  * Each Logger has a "Level" associated with it.  This reflects
  58  * a minimum Level that this logger cares about.  If a Logger's
  59  * level is set to <tt>null</tt>, then its effective level is inherited
  60  * from its parent, which may in turn obtain it recursively from its
  61  * parent, and so on up the tree.
  62  * <p>
  63  * The log level can be configured based on the properties from the
  64  * logging configuration file, as described in the description
  65  * of the LogManager class.  However it may also be dynamically changed
  66  * by calls on the Logger.setLevel method.  If a logger's level is
  67  * changed the change may also affect child loggers, since any child
  68  * logger that has <tt>null</tt> as its level will inherit its
  69  * effective level from its parent.
  70  * <p>
  71  * On each logging call the Logger initially performs a cheap
  72  * check of the request level (e.g., SEVERE or FINE) against the
  73  * effective log level of the logger.  If the request level is
  74  * lower than the log level, the logging call returns immediately.
  75  * <p>
  76  * After passing this initial (cheap) test, the Logger will allocate
  77  * a LogRecord to describe the logging message.  It will then call a
  78  * Filter (if present) to do a more detailed check on whether the
  79  * record should be published.  If that passes it will then publish
  80  * the LogRecord to its output Handlers.  By default, loggers also
  81  * publish to their parent's Handlers, recursively up the tree.
  82  * <p>
  83  * Each Logger may have a ResourceBundle name associated with it.
  84  * The named bundle will be used for localizing logging messages.
  85  * If a Logger does not have its own ResourceBundle name, then
  86  * it will inherit the ResourceBundle name from its parent,
  87  * recursively up the tree.
  88  * <p>
  89  * Most of the logger output methods take a "msg" argument.  This
  90  * msg argument may be either a raw value or a localization key.
  91  * During formatting, if the logger has (or inherits) a localization
  92  * ResourceBundle and if the ResourceBundle has a mapping for the msg
  93  * string, then the msg string is replaced by the localized value.
  94  * Otherwise the original msg string is used.  Typically, formatters use
  95  * java.text.MessageFormat style formatting to format parameters, so
  96  * for example a format string "{0} {1}" would format two parameters
  97  * as strings.
  98  * <p>
  99  * When mapping ResourceBundle names to ResourceBundles, the Logger
 100  * will first try to use the Thread's ContextClassLoader.  If that
 101  * is null it will try the SystemClassLoader instead.  As a temporary
 102  * transition feature in the initial implementation, if the Logger is
 103  * unable to locate a ResourceBundle from the ContextClassLoader or
 104  * SystemClassLoader the Logger will also search up the class stack
 105  * and use successive calling ClassLoaders to try to locate a ResourceBundle.
 106  * (This call stack search is to allow containers to transition to
 107  * using ContextClassLoaders and is likely to be removed in future
 108  * versions.)
 109  * <p>
 110  * Formatting (including localization) is the responsibility of
 111  * the output Handler, which will typically call a Formatter.
 112  * <p>
 113  * Note that formatting need not occur synchronously.  It may be delayed
 114  * until a LogRecord is actually written to an external sink.
 115  * <p>
 116  * The logging methods are grouped in five main categories:
 117  * <ul>
 118  * <li><p>
 119  *     There are a set of "log" methods that take a log level, a message
 120  *     string, and optionally some parameters to the message string.
 121  * <li><p>
 122  *     There are a set of "logp" methods (for "log precise") that are
 123  *     like the "log" methods, but also take an explicit source class name
 124  *     and method name.
 125  * <li><p>
 126  *     There are a set of "logrb" method (for "log with resource bundle")
 127  *     that are like the "logp" method, but also take an explicit resource
 128  *     bundle name for use in localizing the log message.
 129  * <li><p>
 130  *     There are convenience methods for tracing method entries (the
 131  *     "entering" methods), method returns (the "exiting" methods) and
 132  *     throwing exceptions (the "throwing" methods).
 133  * <li><p>
 134  *     Finally, there are a set of convenience methods for use in the
 135  *     very simplest cases, when a developer simply wants to log a
 136  *     simple string at a given log level.  These methods are named
 137  *     after the standard Level names ("severe", "warning", "info", etc.)
 138  *     and take a single argument, a message string.
 139  * </ul>
 140  * <p>
 141  * For the methods that do not take an explicit source name and
 142  * method name, the Logging framework will make a "best effort"
 143  * to determine which class and method called into the logging method.
 144  * However, it is important to realize that this automatically inferred
 145  * information may only be approximate (or may even be quite wrong!).
 146  * Virtual machines are allowed to do extensive optimizations when
 147  * JITing and may entirely remove stack frames, making it impossible
 148  * to reliably locate the calling class and method.
 149  * <P>
 150  * All methods on Logger are multi-thread safe.
 151  * <p>
 152  * <b>Subclassing Information:</b> Note that a LogManager class may
 153  * provide its own implementation of named Loggers for any point in
 154  * the namespace.  Therefore, any subclasses of Logger (unless they
 155  * are implemented in conjunction with a new LogManager class) should
 156  * take care to obtain a Logger instance from the LogManager class and
 157  * should delegate operations such as "isLoggable" and "log(LogRecord)"
 158  * to that instance.  Note that in order to intercept all logging
 159  * output, subclasses need only override the log(LogRecord) method.
 160  * All the other logging methods are implemented as calls on this
 161  * log(LogRecord) method.
 162  *
 163  * @since 1.4
 164  */
 165 
 166 
 167 public class Logger {
 168     private static final Handler emptyHandlers[] = new Handler[0];
 169     private static final int offValue = Level.OFF.intValue();
 170     private LogManager manager;
 171     private String name;
 172     private final CopyOnWriteArrayList<Handler> handlers =
 173         new CopyOnWriteArrayList<>();
 174     private String resourceBundleName;
 175     private volatile boolean useParentHandlers = true;
 176     private volatile Filter filter;
 177     private boolean anonymous;
 178 
 179     private ResourceBundle catalog;     // Cached resource bundle
 180     private String catalogName;         // name associated with catalog
 181     private Locale catalogLocale;       // locale associated with catalog
 182 
 183     // The fields relating to parent-child relationships and levels
 184     // are managed under a separate lock, the treeLock.
 185     private static Object treeLock = new Object();
 186     // We keep weak references from parents to children, but strong
 187     // references from children to parents.
 188     private volatile Logger parent;    // our nearest parent.
 189     private ArrayList<LogManager.LoggerWeakRef> kids;   // WeakReferences to loggers that have us as parent
 190     private volatile Level levelObject;
 191     private volatile int levelValue;  // current effective level value
 192 
 193     /**
 194      * GLOBAL_LOGGER_NAME is a name for the global logger.
 195      *
 196      * @since 1.6
 197      */
 198     public static final String GLOBAL_LOGGER_NAME = "global";
 199 
 200     /**
 201      * Return global logger object with the name Logger.GLOBAL_LOGGER_NAME.
 202      *
 203      * @return global logger object
 204      * @since 1.7
 205      */
 206     public static final Logger getGlobal() {
 207         return global;
 208     }
 209 
 210     /**
 211      * The "global" Logger object is provided as a convenience to developers
 212      * who are making casual use of the Logging package.  Developers
 213      * who are making serious use of the logging package (for example
 214      * in products) should create and use their own Logger objects,
 215      * with appropriate names, so that logging can be controlled on a
 216      * suitable per-Logger granularity. Developers also need to keep a
 217      * strong reference to their Logger objects to prevent them from
 218      * being garbage collected.
 219      * <p>
 220      * @deprecated Initialization of this field is prone to deadlocks.
 221      * The field must be initialized by the Logger class initialization
 222      * which may cause deadlocks with the LogManager class initialization.
 223      * In such cases two class initialization wait for each other to complete.
 224      * The preferred way to get the global logger object is via the call
 225      * <code>Logger.getGlobal()</code>.
 226      * For compatibility with old JDK versions where the
 227      * <code>Logger.getGlobal()</code> is not available use the call
 228      * <code>Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)</code>
 229      * or <code>Logger.getLogger("global")</code>.
 230      */
 231     @Deprecated
 232     public static final Logger global = new Logger(GLOBAL_LOGGER_NAME);
 233 
 234     /**
 235      * Protected method to construct a logger for a named subsystem.
 236      * <p>
 237      * The logger will be initially configured with a null Level
 238      * and with useParentHandlers set to true.
 239      *
 240      * @param   name    A name for the logger.  This should
 241      *                          be a dot-separated name and should normally
 242      *                          be based on the package name or class name
 243      *                          of the subsystem, such as java.net
 244      *                          or javax.swing.  It may be null for anonymous Loggers.
 245      * @param   resourceBundleName  name of ResourceBundle to be used for localizing
 246      *                          messages for this logger.  May be null if none
 247      *                          of the messages require localization.
 248      * @throws MissingResourceException if the resourceBundleName is non-null and
 249      *             no corresponding resource can be found.
 250      */
 251     protected Logger(String name, String resourceBundleName) {
 252         this.manager = LogManager.getLogManager();
 253         if (resourceBundleName != null) {
 254             // Note: we may get a MissingResourceException here.
 255             setupResourceInfo(resourceBundleName);
 256         }
 257         this.name = name;
 258         levelValue = Level.INFO.intValue();
 259     }
 260 
 261     // This constructor is used only to create the global Logger.
 262     // It is needed to break a cyclic dependence between the LogManager
 263     // and Logger static initializers causing deadlocks.
 264     private Logger(String name) {
 265         // The manager field is not initialized here.
 266         this.name = name;
 267         levelValue = Level.INFO.intValue();
 268     }
 269 
 270     // It is called from the LogManager.<clinit> to complete
 271     // initialization of the global Logger.
 272     void setLogManager(LogManager manager) {
 273         this.manager = manager;
 274     }
 275 
 276     private void checkAccess() throws SecurityException {
 277         if (!anonymous) {
 278             if (manager == null) {
 279                 // Complete initialization of the global Logger.
 280                 manager = LogManager.getLogManager();
 281             }
 282             manager.checkAccess();
 283         }
 284     }
 285 
 286     /**
 287      * Find or create a logger for a named subsystem.  If a logger has
 288      * already been created with the given name it is returned.  Otherwise
 289      * a new logger is created.
 290      * <p>
 291      * If a new logger is created its log level will be configured
 292      * based on the LogManager configuration and it will configured
 293      * to also send logging output to its parent's Handlers.  It will
 294      * be registered in the LogManager global namespace.
 295      * <p>
 296      * Note: The LogManager may only retain a weak reference to the newly
 297      * created Logger. It is important to understand that a previously
 298      * created Logger with the given name may be garbage collected at any
 299      * time if there is no strong reference to the Logger. In particular,
 300      * this means that two back-to-back calls like
 301      * {@code getLogger("MyLogger").log(...)} may use different Logger
 302      * objects named "MyLogger" if there is no strong reference to the
 303      * Logger named "MyLogger" elsewhere in the program.
 304      *
 305      * @param   name            A name for the logger.  This should
 306      *                          be a dot-separated name and should normally
 307      *                          be based on the package name or class name
 308      *                          of the subsystem, such as java.net
 309      *                          or javax.swing
 310      * @return a suitable Logger
 311      * @throws NullPointerException if the name is null.
 312      */
 313     public static synchronized Logger getLogger(String name) {
 314         LogManager manager = LogManager.getLogManager();
 315         return manager.demandLogger(name);
 316     }
 317 
 318     /**
 319      * Find or create a logger for a named subsystem.  If a logger has
 320      * already been created with the given name it is returned.  Otherwise
 321      * a new logger is created.
 322      * <p>
 323      * If a new logger is created its log level will be configured
 324      * based on the LogManager and it will configured to also send logging
 325      * output to its parent's Handlers.  It will be registered in
 326      * the LogManager global namespace.
 327      * <p>
 328      * Note: The LogManager may only retain a weak reference to the newly
 329      * created Logger. It is important to understand that a previously
 330      * created Logger with the given name may be garbage collected at any
 331      * time if there is no strong reference to the Logger. In particular,
 332      * this means that two back-to-back calls like
 333      * {@code getLogger("MyLogger", ...).log(...)} may use different Logger
 334      * objects named "MyLogger" if there is no strong reference to the
 335      * Logger named "MyLogger" elsewhere in the program.
 336      * <p>
 337      * If the named Logger already exists and does not yet have a
 338      * localization resource bundle then the given resource bundle
 339      * name is used.  If the named Logger already exists and has
 340      * a different resource bundle name then an IllegalArgumentException
 341      * is thrown.
 342      * <p>
 343      * @param   name    A name for the logger.  This should
 344      *                          be a dot-separated name and should normally
 345      *                          be based on the package name or class name
 346      *                          of the subsystem, such as java.net
 347      *                          or javax.swing
 348      * @param   resourceBundleName  name of ResourceBundle to be used for localizing
 349      *                          messages for this logger. May be <CODE>null</CODE> if none of
 350      *                          the messages require localization.
 351      * @return a suitable Logger
 352      * @throws MissingResourceException if the resourceBundleName is non-null and
 353      *             no corresponding resource can be found.
 354      * @throws IllegalArgumentException if the Logger already exists and uses
 355      *             a different resource bundle name.
 356      * @throws NullPointerException if the name is null.
 357      */
 358     public static synchronized Logger getLogger(String name, String resourceBundleName) {
 359         LogManager manager = LogManager.getLogManager();
 360         Logger result = manager.demandLogger(name);
 361         if (result.resourceBundleName == null) {
 362             // Note: we may get a MissingResourceException here.
 363             result.setupResourceInfo(resourceBundleName);
 364         } else if (!result.resourceBundleName.equals(resourceBundleName)) {
 365             throw new IllegalArgumentException(result.resourceBundleName +
 366                                 " != " + resourceBundleName);
 367         }
 368         return result;
 369     }
 370 
 371 
 372     /**
 373      * Create an anonymous Logger.  The newly created Logger is not
 374      * registered in the LogManager namespace.  There will be no
 375      * access checks on updates to the logger.
 376      * <p>
 377      * This factory method is primarily intended for use from applets.
 378      * Because the resulting Logger is anonymous it can be kept private
 379      * by the creating class.  This removes the need for normal security
 380      * checks, which in turn allows untrusted applet code to update
 381      * the control state of the Logger.  For example an applet can do
 382      * a setLevel or an addHandler on an anonymous Logger.
 383      * <p>
 384      * Even although the new logger is anonymous, it is configured
 385      * to have the root logger ("") as its parent.  This means that
 386      * by default it inherits its effective level and handlers
 387      * from the root logger.
 388      * <p>
 389      *
 390      * @return a newly created private Logger
 391      */
 392     public static Logger getAnonymousLogger() {
 393         return getAnonymousLogger(null);
 394     }
 395 
 396     /**
 397      * Create an anonymous Logger.  The newly created Logger is not
 398      * registered in the LogManager namespace.  There will be no
 399      * access checks on updates to the logger.
 400      * <p>
 401      * This factory method is primarily intended for use from applets.
 402      * Because the resulting Logger is anonymous it can be kept private
 403      * by the creating class.  This removes the need for normal security
 404      * checks, which in turn allows untrusted applet code to update
 405      * the control state of the Logger.  For example an applet can do
 406      * a setLevel or an addHandler on an anonymous Logger.
 407      * <p>
 408      * Even although the new logger is anonymous, it is configured
 409      * to have the root logger ("") as its parent.  This means that
 410      * by default it inherits its effective level and handlers
 411      * from the root logger.
 412      * <p>
 413      * @param   resourceBundleName  name of ResourceBundle to be used for localizing
 414      *                          messages for this logger.
 415      *          May be null if none of the messages require localization.
 416      * @return a newly created private Logger
 417      * @throws MissingResourceException if the resourceBundleName is non-null and
 418      *             no corresponding resource can be found.
 419      */
 420     public static synchronized Logger getAnonymousLogger(String resourceBundleName) {
 421         LogManager manager = LogManager.getLogManager();
 422         // cleanup some Loggers that have been GC'ed
 423         manager.drainLoggerRefQueueBounded();
 424         Logger result = new Logger(null, resourceBundleName);
 425         result.anonymous = true;
 426         Logger root = manager.getLogger("");
 427         result.doSetParent(root);
 428         return result;
 429     }
 430 
 431     /**
 432      * Retrieve the localization resource bundle for this
 433      * logger for the current default locale.  Note that if
 434      * the result is null, then the Logger will use a resource
 435      * bundle inherited from its parent.
 436      *
 437      * @return localization bundle (may be null)
 438      */
 439     public ResourceBundle getResourceBundle() {
 440         return findResourceBundle(getResourceBundleName());
 441     }
 442 
 443     /**
 444      * Retrieve the localization resource bundle name for this
 445      * logger.  Note that if the result is null, then the Logger
 446      * will use a resource bundle name inherited from its parent.
 447      *
 448      * @return localization bundle name (may be null)
 449      */
 450     public String getResourceBundleName() {
 451         return resourceBundleName;
 452     }
 453 
 454     /**
 455      * Set a filter to control output on this Logger.
 456      * <P>
 457      * After passing the initial "level" check, the Logger will
 458      * call this Filter to check if a log record should really
 459      * be published.
 460      *
 461      * @param   newFilter  a filter object (may be null)
 462      * @exception  SecurityException  if a security manager exists and if
 463      *             the caller does not have LoggingPermission("control").
 464      */
 465     public void setFilter(Filter newFilter) throws SecurityException {
 466         checkAccess();
 467         filter = newFilter;
 468     }
 469 
 470     /**
 471      * Get the current filter for this Logger.
 472      *
 473      * @return  a filter object (may be null)
 474      */
 475     public Filter getFilter() {
 476         return filter;
 477     }
 478 
 479     /**
 480      * Log a LogRecord.
 481      * <p>
 482      * All the other logging methods in this class call through
 483      * this method to actually perform any logging.  Subclasses can
 484      * override this single method to capture all log activity.
 485      *
 486      * @param record the LogRecord to be published
 487      */
 488     public void log(LogRecord record) {
 489         if (record.getLevel().intValue() < levelValue || levelValue == offValue) {
 490             return;
 491         }
 492         Filter theFilter = filter;
 493         if (theFilter != null && !theFilter.isLoggable(record)) {
 494             return;
 495         }
 496 
 497         // Post the LogRecord to all our Handlers, and then to
 498         // our parents' handlers, all the way up the tree.
 499 
 500         Logger logger = this;
 501         while (logger != null) {
 502             for (Handler handler : logger.getHandlers()) {
 503                 handler.publish(record);
 504             }
 505 
 506             if (!logger.getUseParentHandlers()) {
 507                 break;
 508             }
 509 
 510             logger = logger.getParent();
 511         }
 512     }
 513 
 514     // private support method for logging.
 515     // We fill in the logger name, resource bundle name, and
 516     // resource bundle and then call "void log(LogRecord)".
 517     private void doLog(LogRecord lr) {
 518         lr.setLoggerName(name);
 519         String ebname = getEffectiveResourceBundleName();
 520         if (ebname != null) {
 521             lr.setResourceBundleName(ebname);
 522             lr.setResourceBundle(findResourceBundle(ebname));
 523         }
 524         log(lr);
 525     }
 526 
 527 
 528     //================================================================
 529     // Start of convenience methods WITHOUT className and methodName
 530     //================================================================
 531 
 532     /**
 533      * Log a message, with no arguments.
 534      * <p>
 535      * If the logger is currently enabled for the given message
 536      * level then the given message is forwarded to all the
 537      * registered output Handler objects.
 538      * <p>
 539      * @param   level   One of the message level identifiers, e.g., SEVERE
 540      * @param   msg     The string message (or a key in the message catalog)
 541      */
 542     public void log(Level level, String msg) {
 543         if (level.intValue() < levelValue || levelValue == offValue) {
 544             return;
 545         }
 546         LogRecord lr = new LogRecord(level, msg);
 547         doLog(lr);
 548     }
 549 
 550     /**
 551      * Log a message, with one object parameter.
 552      * <p>
 553      * If the logger is currently enabled for the given message
 554      * level then a corresponding LogRecord is created and forwarded
 555      * to all the registered output Handler objects.
 556      * <p>
 557      * @param   level   One of the message level identifiers, e.g., SEVERE
 558      * @param   msg     The string message (or a key in the message catalog)
 559      * @param   param1  parameter to the message
 560      */
 561     public void log(Level level, String msg, Object param1) {
 562         if (level.intValue() < levelValue || levelValue == offValue) {
 563             return;
 564         }
 565         LogRecord lr = new LogRecord(level, msg);
 566         Object params[] = { param1 };
 567         lr.setParameters(params);
 568         doLog(lr);
 569     }
 570 
 571     /**
 572      * Log a message, with an array of object arguments.
 573      * <p>
 574      * If the logger is currently enabled for the given message
 575      * level then a corresponding LogRecord is created and forwarded
 576      * to all the registered output Handler objects.
 577      * <p>
 578      * @param   level   One of the message level identifiers, e.g., SEVERE
 579      * @param   msg     The string message (or a key in the message catalog)
 580      * @param   params  array of parameters to the message
 581      */
 582     public void log(Level level, String msg, Object params[]) {
 583         if (level.intValue() < levelValue || levelValue == offValue) {
 584             return;
 585         }
 586         LogRecord lr = new LogRecord(level, msg);
 587         lr.setParameters(params);
 588         doLog(lr);
 589     }
 590 
 591     /**
 592      * Log a message, with associated Throwable information.
 593      * <p>
 594      * If the logger is currently enabled for the given message
 595      * level then the given arguments are stored in a LogRecord
 596      * which is forwarded to all registered output handlers.
 597      * <p>
 598      * Note that the thrown argument is stored in the LogRecord thrown
 599      * property, rather than the LogRecord parameters property.  Thus is it
 600      * processed specially by output Formatters and is not treated
 601      * as a formatting parameter to the LogRecord message property.
 602      * <p>
 603      * @param   level   One of the message level identifiers, e.g., SEVERE
 604      * @param   msg     The string message (or a key in the message catalog)
 605      * @param   thrown  Throwable associated with log message.
 606      */
 607     public void log(Level level, String msg, Throwable thrown) {
 608         if (level.intValue() < levelValue || levelValue == offValue) {
 609             return;
 610         }
 611         LogRecord lr = new LogRecord(level, msg);
 612         lr.setThrown(thrown);
 613         doLog(lr);
 614     }
 615 
 616     //================================================================
 617     // Start of convenience methods WITH className and methodName
 618     //================================================================
 619 
 620     /**
 621      * Log a message, specifying source class and method,
 622      * with no arguments.
 623      * <p>
 624      * If the logger is currently enabled for the given message
 625      * level then the given message is forwarded to all the
 626      * registered output Handler objects.
 627      * <p>
 628      * @param   level   One of the message level identifiers, e.g., SEVERE
 629      * @param   sourceClass    name of class that issued the logging request
 630      * @param   sourceMethod   name of method that issued the logging request
 631      * @param   msg     The string message (or a key in the message catalog)
 632      */
 633     public void logp(Level level, String sourceClass, String sourceMethod, String msg) {
 634         if (level.intValue() < levelValue || levelValue == offValue) {
 635             return;
 636         }
 637         LogRecord lr = new LogRecord(level, msg);
 638         lr.setSourceClassName(sourceClass);
 639         lr.setSourceMethodName(sourceMethod);
 640         doLog(lr);
 641     }
 642 
 643     /**
 644      * Log a message, specifying source class and method,
 645      * with a single object parameter to the log message.
 646      * <p>
 647      * If the logger is currently enabled for the given message
 648      * level then a corresponding LogRecord is created and forwarded
 649      * to all the registered output Handler objects.
 650      * <p>
 651      * @param   level   One of the message level identifiers, e.g., SEVERE
 652      * @param   sourceClass    name of class that issued the logging request
 653      * @param   sourceMethod   name of method that issued the logging request
 654      * @param   msg      The string message (or a key in the message catalog)
 655      * @param   param1    Parameter to the log message.
 656      */
 657     public void logp(Level level, String sourceClass, String sourceMethod,
 658                                                 String msg, Object param1) {
 659         if (level.intValue() < levelValue || levelValue == offValue) {
 660             return;
 661         }
 662         LogRecord lr = new LogRecord(level, msg);
 663         lr.setSourceClassName(sourceClass);
 664         lr.setSourceMethodName(sourceMethod);
 665         Object params[] = { param1 };
 666         lr.setParameters(params);
 667         doLog(lr);
 668     }
 669 
 670     /**
 671      * Log a message, specifying source class and method,
 672      * with an array of object arguments.
 673      * <p>
 674      * If the logger is currently enabled for the given message
 675      * level then a corresponding LogRecord is created and forwarded
 676      * to all the registered output Handler objects.
 677      * <p>
 678      * @param   level   One of the message level identifiers, e.g., SEVERE
 679      * @param   sourceClass    name of class that issued the logging request
 680      * @param   sourceMethod   name of method that issued the logging request
 681      * @param   msg     The string message (or a key in the message catalog)
 682      * @param   params  Array of parameters to the message
 683      */
 684     public void logp(Level level, String sourceClass, String sourceMethod,
 685                                                 String msg, Object params[]) {
 686         if (level.intValue() < levelValue || levelValue == offValue) {
 687             return;
 688         }
 689         LogRecord lr = new LogRecord(level, msg);
 690         lr.setSourceClassName(sourceClass);
 691         lr.setSourceMethodName(sourceMethod);
 692         lr.setParameters(params);
 693         doLog(lr);
 694     }
 695 
 696     /**
 697      * Log a message, specifying source class and method,
 698      * with associated Throwable information.
 699      * <p>
 700      * If the logger is currently enabled for the given message
 701      * level then the given arguments are stored in a LogRecord
 702      * which is forwarded to all registered output handlers.
 703      * <p>
 704      * Note that the thrown argument is stored in the LogRecord thrown
 705      * property, rather than the LogRecord parameters property.  Thus is it
 706      * processed specially by output Formatters and is not treated
 707      * as a formatting parameter to the LogRecord message property.
 708      * <p>
 709      * @param   level   One of the message level identifiers, e.g., SEVERE
 710      * @param   sourceClass    name of class that issued the logging request
 711      * @param   sourceMethod   name of method that issued the logging request
 712      * @param   msg     The string message (or a key in the message catalog)
 713      * @param   thrown  Throwable associated with log message.
 714      */
 715     public void logp(Level level, String sourceClass, String sourceMethod,
 716                                                         String msg, Throwable thrown) {
 717         if (level.intValue() < levelValue || levelValue == offValue) {
 718             return;
 719         }
 720         LogRecord lr = new LogRecord(level, msg);
 721         lr.setSourceClassName(sourceClass);
 722         lr.setSourceMethodName(sourceMethod);
 723         lr.setThrown(thrown);
 724         doLog(lr);
 725     }
 726 
 727 
 728     //=========================================================================
 729     // Start of convenience methods WITH className, methodName and bundle name.
 730     //=========================================================================
 731 
 732     // Private support method for logging for "logrb" methods.
 733     // We fill in the logger name, resource bundle name, and
 734     // resource bundle and then call "void log(LogRecord)".
 735     private void doLog(LogRecord lr, String rbname) {
 736         lr.setLoggerName(name);
 737         if (rbname != null) {
 738             lr.setResourceBundleName(rbname);
 739             lr.setResourceBundle(findResourceBundle(rbname));
 740         }
 741         log(lr);
 742     }
 743 
 744     /**
 745      * Log a message, specifying source class, method, and resource bundle name
 746      * with no arguments.
 747      * <p>
 748      * If the logger is currently enabled for the given message
 749      * level then the given message is forwarded to all the
 750      * registered output Handler objects.
 751      * <p>
 752      * The msg string is localized using the named resource bundle.  If the
 753      * resource bundle name is null, or an empty String or invalid
 754      * then the msg string is not localized.
 755      * <p>
 756      * @param   level   One of the message level identifiers, e.g., SEVERE
 757      * @param   sourceClass    name of class that issued the logging request
 758      * @param   sourceMethod   name of method that issued the logging request
 759      * @param   bundleName     name of resource bundle to localize msg,
 760      *                         can be null
 761      * @param   msg     The string message (or a key in the message catalog)
 762      */
 763 
 764     public void logrb(Level level, String sourceClass, String sourceMethod,
 765                                 String bundleName, String msg) {
 766         if (level.intValue() < levelValue || levelValue == offValue) {
 767             return;
 768         }
 769         LogRecord lr = new LogRecord(level, msg);
 770         lr.setSourceClassName(sourceClass);
 771         lr.setSourceMethodName(sourceMethod);
 772         doLog(lr, bundleName);
 773     }
 774 
 775     /**
 776      * Log a message, specifying source class, method, and resource bundle name,
 777      * with a single object parameter to the log message.
 778      * <p>
 779      * If the logger is currently enabled for the given message
 780      * level then a corresponding LogRecord is created and forwarded
 781      * to all the registered output Handler objects.
 782      * <p>
 783      * The msg string is localized using the named resource bundle.  If the
 784      * resource bundle name is null, or an empty String or invalid
 785      * then the msg string is not localized.
 786      * <p>
 787      * @param   level   One of the message level identifiers, e.g., SEVERE
 788      * @param   sourceClass    name of class that issued the logging request
 789      * @param   sourceMethod   name of method that issued the logging request
 790      * @param   bundleName     name of resource bundle to localize msg,
 791      *                         can be null
 792      * @param   msg      The string message (or a key in the message catalog)
 793      * @param   param1    Parameter to the log message.
 794      */
 795     public void logrb(Level level, String sourceClass, String sourceMethod,
 796                                 String bundleName, String msg, Object param1) {
 797         if (level.intValue() < levelValue || levelValue == offValue) {
 798             return;
 799         }
 800         LogRecord lr = new LogRecord(level, msg);
 801         lr.setSourceClassName(sourceClass);
 802         lr.setSourceMethodName(sourceMethod);
 803         Object params[] = { param1 };
 804         lr.setParameters(params);
 805         doLog(lr, bundleName);
 806     }
 807 
 808     /**
 809      * Log a message, specifying source class, method, and resource bundle name,
 810      * with an array of object arguments.
 811      * <p>
 812      * If the logger is currently enabled for the given message
 813      * level then a corresponding LogRecord is created and forwarded
 814      * to all the registered output Handler objects.
 815      * <p>
 816      * The msg string is localized using the named resource bundle.  If the
 817      * resource bundle name is null, or an empty String or invalid
 818      * then the msg string is not localized.
 819      * <p>
 820      * @param   level   One of the message level identifiers, e.g., SEVERE
 821      * @param   sourceClass    name of class that issued the logging request
 822      * @param   sourceMethod   name of method that issued the logging request
 823      * @param   bundleName     name of resource bundle to localize msg,
 824      *                         can be null.
 825      * @param   msg     The string message (or a key in the message catalog)
 826      * @param   params  Array of parameters to the message
 827      */
 828     public void logrb(Level level, String sourceClass, String sourceMethod,
 829                                 String bundleName, String msg, Object params[]) {
 830         if (level.intValue() < levelValue || levelValue == offValue) {
 831             return;
 832         }
 833         LogRecord lr = new LogRecord(level, msg);
 834         lr.setSourceClassName(sourceClass);
 835         lr.setSourceMethodName(sourceMethod);
 836         lr.setParameters(params);
 837         doLog(lr, bundleName);
 838     }
 839 
 840     /**
 841      * Log a message, specifying source class, method, and resource bundle name,
 842      * with associated Throwable information.
 843      * <p>
 844      * If the logger is currently enabled for the given message
 845      * level then the given arguments are stored in a LogRecord
 846      * which is forwarded to all registered output handlers.
 847      * <p>
 848      * The msg string is localized using the named resource bundle.  If the
 849      * resource bundle name is null, or an empty String or invalid
 850      * then the msg string is not localized.
 851      * <p>
 852      * Note that the thrown argument is stored in the LogRecord thrown
 853      * property, rather than the LogRecord parameters property.  Thus is it
 854      * processed specially by output Formatters and is not treated
 855      * as a formatting parameter to the LogRecord message property.
 856      * <p>
 857      * @param   level   One of the message level identifiers, e.g., SEVERE
 858      * @param   sourceClass    name of class that issued the logging request
 859      * @param   sourceMethod   name of method that issued the logging request
 860      * @param   bundleName     name of resource bundle to localize msg,
 861      *                         can be null
 862      * @param   msg     The string message (or a key in the message catalog)
 863      * @param   thrown  Throwable associated with log message.
 864      */
 865     public void logrb(Level level, String sourceClass, String sourceMethod,
 866                                         String bundleName, String msg, Throwable thrown) {
 867         if (level.intValue() < levelValue || levelValue == offValue) {
 868             return;
 869         }
 870         LogRecord lr = new LogRecord(level, msg);
 871         lr.setSourceClassName(sourceClass);
 872         lr.setSourceMethodName(sourceMethod);
 873         lr.setThrown(thrown);
 874         doLog(lr, bundleName);
 875     }
 876 
 877 
 878     //======================================================================
 879     // Start of convenience methods for logging method entries and returns.
 880     //======================================================================
 881 
 882     /**
 883      * Log a method entry.
 884      * <p>
 885      * This is a convenience method that can be used to log entry
 886      * to a method.  A LogRecord with message "ENTRY", log level
 887      * FINER, and the given sourceMethod and sourceClass is logged.
 888      * <p>
 889      * @param   sourceClass    name of class that issued the logging request
 890      * @param   sourceMethod   name of method that is being entered
 891      */
 892     public void entering(String sourceClass, String sourceMethod) {
 893         if (Level.FINER.intValue() < levelValue) {
 894             return;
 895         }
 896         logp(Level.FINER, sourceClass, sourceMethod, "ENTRY");
 897     }
 898 
 899     /**
 900      * Log a method entry, with one parameter.
 901      * <p>
 902      * This is a convenience method that can be used to log entry
 903      * to a method.  A LogRecord with message "ENTRY {0}", log level
 904      * FINER, and the given sourceMethod, sourceClass, and parameter
 905      * is logged.
 906      * <p>
 907      * @param   sourceClass    name of class that issued the logging request
 908      * @param   sourceMethod   name of method that is being entered
 909      * @param   param1         parameter to the method being entered
 910      */
 911     public void entering(String sourceClass, String sourceMethod, Object param1) {
 912         if (Level.FINER.intValue() < levelValue) {
 913             return;
 914         }
 915         Object params[] = { param1 };
 916         logp(Level.FINER, sourceClass, sourceMethod, "ENTRY {0}", params);
 917     }
 918 
 919     /**
 920      * Log a method entry, with an array of parameters.
 921      * <p>
 922      * This is a convenience method that can be used to log entry
 923      * to a method.  A LogRecord with message "ENTRY" (followed by a
 924      * format {N} indicator for each entry in the parameter array),
 925      * log level FINER, and the given sourceMethod, sourceClass, and
 926      * parameters is logged.
 927      * <p>
 928      * @param   sourceClass    name of class that issued the logging request
 929      * @param   sourceMethod   name of method that is being entered
 930      * @param   params         array of parameters to the method being entered
 931      */
 932     public void entering(String sourceClass, String sourceMethod, Object params[]) {
 933         if (Level.FINER.intValue() < levelValue) {
 934             return;
 935         }
 936         String msg = "ENTRY";
 937         if (params == null ) {
 938            logp(Level.FINER, sourceClass, sourceMethod, msg);
 939            return;
 940         }
 941         for (int i = 0; i < params.length; i++) {
 942             msg = msg + " {" + i + "}";
 943         }
 944         logp(Level.FINER, sourceClass, sourceMethod, msg, params);
 945     }
 946 
 947     /**
 948      * Log a method return.
 949      * <p>
 950      * This is a convenience method that can be used to log returning
 951      * from a method.  A LogRecord with message "RETURN", log level
 952      * FINER, and the given sourceMethod and sourceClass is logged.
 953      * <p>
 954      * @param   sourceClass    name of class that issued the logging request
 955      * @param   sourceMethod   name of the method
 956      */
 957     public void exiting(String sourceClass, String sourceMethod) {
 958         if (Level.FINER.intValue() < levelValue) {
 959             return;
 960         }
 961         logp(Level.FINER, sourceClass, sourceMethod, "RETURN");
 962     }
 963 
 964 
 965     /**
 966      * Log a method return, with result object.
 967      * <p>
 968      * This is a convenience method that can be used to log returning
 969      * from a method.  A LogRecord with message "RETURN {0}", log level
 970      * FINER, and the gives sourceMethod, sourceClass, and result
 971      * object is logged.
 972      * <p>
 973      * @param   sourceClass    name of class that issued the logging request
 974      * @param   sourceMethod   name of the method
 975      * @param   result  Object that is being returned
 976      */
 977     public void exiting(String sourceClass, String sourceMethod, Object result) {
 978         if (Level.FINER.intValue() < levelValue) {
 979             return;
 980         }
 981         Object params[] = { result };
 982         logp(Level.FINER, sourceClass, sourceMethod, "RETURN {0}", result);
 983     }
 984 
 985     /**
 986      * Log throwing an exception.
 987      * <p>
 988      * This is a convenience method to log that a method is
 989      * terminating by throwing an exception.  The logging is done
 990      * using the FINER level.
 991      * <p>
 992      * If the logger is currently enabled for the given message
 993      * level then the given arguments are stored in a LogRecord
 994      * which is forwarded to all registered output handlers.  The
 995      * LogRecord's message is set to "THROW".
 996      * <p>
 997      * Note that the thrown argument is stored in the LogRecord thrown
 998      * property, rather than the LogRecord parameters property.  Thus is it
 999      * processed specially by output Formatters and is not treated
1000      * as a formatting parameter to the LogRecord message property.
1001      * <p>
1002      * @param   sourceClass    name of class that issued the logging request
1003      * @param   sourceMethod  name of the method.
1004      * @param   thrown  The Throwable that is being thrown.
1005      */
1006     public void throwing(String sourceClass, String sourceMethod, Throwable thrown) {
1007         if (Level.FINER.intValue() < levelValue || levelValue == offValue ) {
1008             return;
1009         }
1010         LogRecord lr = new LogRecord(Level.FINER, "THROW");
1011         lr.setSourceClassName(sourceClass);
1012         lr.setSourceMethodName(sourceMethod);
1013         lr.setThrown(thrown);
1014         doLog(lr);
1015     }
1016 
1017     //=======================================================================
1018     // Start of simple convenience methods using level names as method names
1019     //=======================================================================
1020 
1021     /**
1022      * Log a SEVERE message.
1023      * <p>
1024      * If the logger is currently enabled for the SEVERE message
1025      * level then the given message is forwarded to all the
1026      * registered output Handler objects.
1027      * <p>
1028      * @param   msg     The string message (or a key in the message catalog)
1029      */
1030     public void severe(String msg) {
1031         if (Level.SEVERE.intValue() < levelValue) {
1032             return;
1033         }
1034         log(Level.SEVERE, msg);
1035     }
1036 
1037     /**
1038      * Log a WARNING message.
1039      * <p>
1040      * If the logger is currently enabled for the WARNING message
1041      * level then the given message is forwarded to all the
1042      * registered output Handler objects.
1043      * <p>
1044      * @param   msg     The string message (or a key in the message catalog)
1045      */
1046     public void warning(String msg) {
1047         if (Level.WARNING.intValue() < levelValue) {
1048             return;
1049         }
1050         log(Level.WARNING, msg);
1051     }
1052 
1053     /**
1054      * Log an INFO message.
1055      * <p>
1056      * If the logger is currently enabled for the INFO message
1057      * level then the given message is forwarded to all the
1058      * registered output Handler objects.
1059      * <p>
1060      * @param   msg     The string message (or a key in the message catalog)
1061      */
1062     public void info(String msg) {
1063         if (Level.INFO.intValue() < levelValue) {
1064             return;
1065         }
1066         log(Level.INFO, msg);
1067     }
1068 
1069     /**
1070      * Log a CONFIG message.
1071      * <p>
1072      * If the logger is currently enabled for the CONFIG message
1073      * level then the given message is forwarded to all the
1074      * registered output Handler objects.
1075      * <p>
1076      * @param   msg     The string message (or a key in the message catalog)
1077      */
1078     public void config(String msg) {
1079         if (Level.CONFIG.intValue() < levelValue) {
1080             return;
1081         }
1082         log(Level.CONFIG, msg);
1083     }
1084 
1085     /**
1086      * Log a FINE message.
1087      * <p>
1088      * If the logger is currently enabled for the FINE message
1089      * level then the given message is forwarded to all the
1090      * registered output Handler objects.
1091      * <p>
1092      * @param   msg     The string message (or a key in the message catalog)
1093      */
1094     public void fine(String msg) {
1095         if (Level.FINE.intValue() < levelValue) {
1096             return;
1097         }
1098         log(Level.FINE, msg);
1099     }
1100 
1101     /**
1102      * Log a FINER message.
1103      * <p>
1104      * If the logger is currently enabled for the FINER message
1105      * level then the given message is forwarded to all the
1106      * registered output Handler objects.
1107      * <p>
1108      * @param   msg     The string message (or a key in the message catalog)
1109      */
1110     public void finer(String msg) {
1111         if (Level.FINER.intValue() < levelValue) {
1112             return;
1113         }
1114         log(Level.FINER, msg);
1115     }
1116 
1117     /**
1118      * Log a FINEST message.
1119      * <p>
1120      * If the logger is currently enabled for the FINEST message
1121      * level then the given message is forwarded to all the
1122      * registered output Handler objects.
1123      * <p>
1124      * @param   msg     The string message (or a key in the message catalog)
1125      */
1126     public void finest(String msg) {
1127         if (Level.FINEST.intValue() < levelValue) {
1128             return;
1129         }
1130         log(Level.FINEST, msg);
1131     }
1132 
1133     //================================================================
1134     // End of convenience methods
1135     //================================================================
1136 
1137     /**
1138      * Set the log level specifying which message levels will be
1139      * logged by this logger.  Message levels lower than this
1140      * value will be discarded.  The level value Level.OFF
1141      * can be used to turn off logging.
1142      * <p>
1143      * If the new level is null, it means that this node should
1144      * inherit its level from its nearest ancestor with a specific
1145      * (non-null) level value.
1146      *
1147      * @param newLevel   the new value for the log level (may be null)
1148      * @exception  SecurityException  if a security manager exists and if
1149      *             the caller does not have LoggingPermission("control").
1150      */
1151     public void setLevel(Level newLevel) throws SecurityException {
1152         checkAccess();
1153         synchronized (treeLock) {
1154             levelObject = newLevel;
1155             updateEffectiveLevel();
1156         }
1157     }
1158 
1159     /**
1160      * Get the log Level that has been specified for this Logger.
1161      * The result may be null, which means that this logger's
1162      * effective level will be inherited from its parent.
1163      *
1164      * @return  this Logger's level
1165      */
1166     public Level getLevel() {
1167         return levelObject;
1168     }
1169 
1170     /**
1171      * Check if a message of the given level would actually be logged
1172      * by this logger.  This check is based on the Loggers effective level,
1173      * which may be inherited from its parent.
1174      *
1175      * @param   level   a message logging level
1176      * @return  true if the given message level is currently being logged.
1177      */
1178     public boolean isLoggable(Level level) {
1179         if (level.intValue() < levelValue || levelValue == offValue) {
1180             return false;
1181         }
1182         return true;
1183     }
1184 
1185     /**
1186      * Get the name for this logger.
1187      * @return logger name.  Will be null for anonymous Loggers.
1188      */
1189     public String getName() {
1190         return name;
1191     }
1192 
1193     /**
1194      * Add a log Handler to receive logging messages.
1195      * <p>
1196      * By default, Loggers also send their output to their parent logger.
1197      * Typically the root Logger is configured with a set of Handlers
1198      * that essentially act as default handlers for all loggers.
1199      *
1200      * @param   handler a logging Handler
1201      * @exception  SecurityException  if a security manager exists and if
1202      *             the caller does not have LoggingPermission("control").
1203      */
1204     public void addHandler(Handler handler) throws SecurityException {
1205         // Check for null handler
1206         handler.getClass();
1207         checkAccess();
1208         handlers.add(handler);
1209     }
1210 
1211     /**
1212      * Remove a log Handler.
1213      * <P>
1214      * Returns silently if the given Handler is not found or is null
1215      *
1216      * @param   handler a logging Handler
1217      * @exception  SecurityException  if a security manager exists and if
1218      *             the caller does not have LoggingPermission("control").
1219      */
1220     public void removeHandler(Handler handler) throws SecurityException {
1221         checkAccess();
1222         if (handler == null) {
1223             return;
1224         }
1225         handlers.remove(handler);
1226     }
1227 
1228     /**
1229      * Get the Handlers associated with this logger.
1230      * <p>
1231      * @return  an array of all registered Handlers
1232      */
1233     public Handler[] getHandlers() {
1234         return handlers.toArray(emptyHandlers);
1235     }
1236 
1237     /**
1238      * Specify whether or not this logger should send its output
1239      * to its parent Logger.  This means that any LogRecords will
1240      * also be written to the parent's Handlers, and potentially
1241      * to its parent, recursively up the namespace.
1242      *
1243      * @param useParentHandlers   true if output is to be sent to the
1244      *          logger's parent.
1245      * @exception  SecurityException  if a security manager exists and if
1246      *             the caller does not have LoggingPermission("control").
1247      */
1248     public void setUseParentHandlers(boolean useParentHandlers) {
1249         checkAccess();
1250         this.useParentHandlers = useParentHandlers;
1251     }
1252 
1253     /**
1254      * Discover whether or not this logger is sending its output
1255      * to its parent logger.
1256      *
1257      * @return  true if output is to be sent to the logger's parent
1258      */
1259     public boolean getUseParentHandlers() {
1260         return useParentHandlers;
1261     }
1262 
1263     // Private utility method to map a resource bundle name to an
1264     // actual resource bundle, using a simple one-entry cache.
1265     // Returns null for a null name.
1266     // May also return null if we can't find the resource bundle and
1267     // there is no suitable previous cached value.
1268 
1269     private synchronized ResourceBundle findResourceBundle(String name) {
1270         // Return a null bundle for a null name.
1271         if (name == null) {
1272             return null;
1273         }
1274 
1275         Locale currentLocale = Locale.getDefault();
1276 
1277         // Normally we should hit on our simple one entry cache.
1278         if (catalog != null && currentLocale == catalogLocale
1279                                         && name == catalogName) {
1280             return catalog;
1281         }
1282 
1283         // Use the thread's context ClassLoader.  If there isn't one,
1284         // use the SystemClassloader.
1285         ClassLoader cl = Thread.currentThread().getContextClassLoader();
1286         if (cl == null) {
1287             cl = ClassLoader.getSystemClassLoader();
1288         }
1289         try {
1290             catalog = ResourceBundle.getBundle(name, currentLocale, cl);
1291             catalogName = name;
1292             catalogLocale = currentLocale;
1293             return catalog;
1294         } catch (MissingResourceException ex) {
1295             // Woops.  We can't find the ResourceBundle in the default
1296             // ClassLoader.  Drop through.
1297         }
1298 
1299 
1300         // Fall back to searching up the call stack and trying each
1301         // calling ClassLoader.
1302         for (int ix = 0; ; ix++) {
1303             Class clz = sun.reflect.Reflection.getCallerClass(ix);
1304             if (clz == null) {
1305                 break;
1306             }
1307             ClassLoader cl2 = clz.getClassLoader();
1308             if (cl2 == null) {
1309                 cl2 = ClassLoader.getSystemClassLoader();
1310             }
1311             if (cl == cl2) {
1312                 // We've already checked this classloader.
1313                 continue;
1314             }
1315             cl = cl2;
1316             try {
1317                 catalog = ResourceBundle.getBundle(name, currentLocale, cl);
1318                 catalogName = name;
1319                 catalogLocale = currentLocale;
1320                 return catalog;
1321             } catch (MissingResourceException ex) {
1322                 // Ok, this one didn't work either.
1323                 // Drop through, and try the next one.
1324             }
1325         }
1326 
1327         if (name.equals(catalogName)) {
1328             // Return the previous cached value for that name.
1329             // This may be null.
1330             return catalog;
1331         }
1332         // Sorry, we're out of luck.
1333         return null;
1334     }
1335 
1336     // Private utility method to initialize our one entry
1337     // resource bundle cache.
1338     // Note: for consistency reasons, we are careful to check
1339     // that a suitable ResourceBundle exists before setting the
1340     // ResourceBundleName.
1341     private synchronized void setupResourceInfo(String name) {
1342         if (name == null) {
1343             return;
1344         }
1345         ResourceBundle rb = findResourceBundle(name);
1346         if (rb == null) {
1347             // We've failed to find an expected ResourceBundle.
1348             throw new MissingResourceException("Can't find " + name + " bundle", name, "");
1349         }
1350         resourceBundleName = name;
1351     }
1352 
1353     /**
1354      * Return the parent for this Logger.
1355      * <p>
1356      * This method returns the nearest extant parent in the namespace.
1357      * Thus if a Logger is called "a.b.c.d", and a Logger called "a.b"
1358      * has been created but no logger "a.b.c" exists, then a call of
1359      * getParent on the Logger "a.b.c.d" will return the Logger "a.b".
1360      * <p>
1361      * The result will be null if it is called on the root Logger
1362      * in the namespace.
1363      *
1364      * @return nearest existing parent Logger
1365      */
1366     public Logger getParent() {
1367         // Note: this used to be synchronized on treeLock.  However, this only
1368         // provided memory semantics, as there was no guarantee that the caller
1369         // would synchronize on treeLock (in fact, there is no way for external
1370         // callers to so synchronize).  Therefore, we have made parent volatile
1371         // instead.
1372         return parent;
1373     }
1374 
1375     /**
1376      * Set the parent for this Logger.  This method is used by
1377      * the LogManager to update a Logger when the namespace changes.
1378      * <p>
1379      * It should not be called from application code.
1380      * <p>
1381      * @param  parent   the new parent logger
1382      * @exception  SecurityException  if a security manager exists and if
1383      *             the caller does not have LoggingPermission("control").
1384      */
1385     public void setParent(Logger parent) {
1386         if (parent == null) {
1387             throw new NullPointerException();
1388         }
1389         manager.checkAccess();
1390         doSetParent(parent);
1391     }
1392 
1393     // Private method to do the work for parenting a child
1394     // Logger onto a parent logger.
1395     private void doSetParent(Logger newParent) {
1396 
1397         // System.err.println("doSetParent \"" + getName() + "\" \""
1398         //                              + newParent.getName() + "\"");
1399 
1400         synchronized (treeLock) {
1401 
1402             // Remove ourself from any previous parent.
1403             LogManager.LoggerWeakRef ref = null;
1404             if (parent != null) {
1405                 // assert parent.kids != null;
1406                 for (Iterator<LogManager.LoggerWeakRef> iter = parent.kids.iterator(); iter.hasNext(); ) {
1407                     ref = iter.next();
1408                     Logger kid =  ref.get();
1409                     if (kid == this) {
1410                         // ref is used down below to complete the reparenting
1411                         iter.remove();
1412                         break;
1413                     } else {
1414                         ref = null;
1415                     }
1416                 }
1417                 // We have now removed ourself from our parents' kids.
1418             }
1419 
1420             // Set our new parent.
1421             parent = newParent;
1422             if (parent.kids == null) {
1423                 parent.kids = new ArrayList<>(2);
1424             }
1425             if (ref == null) {
1426                 // we didn't have a previous parent
1427                 ref = manager.new LoggerWeakRef(this);
1428             }
1429             ref.setParentRef(new WeakReference<Logger>(parent));
1430             parent.kids.add(ref);
1431 
1432             // As a result of the reparenting, the effective level
1433             // may have changed for us and our children.
1434             updateEffectiveLevel();
1435 
1436         }
1437     }
1438 
1439     // Package-level method.
1440     // Remove the weak reference for the specified child Logger from the
1441     // kid list. We should only be called from LoggerWeakRef.dispose().
1442     final void removeChildLogger(LogManager.LoggerWeakRef child) {
1443         synchronized (treeLock) {
1444             for (Iterator<LogManager.LoggerWeakRef> iter = kids.iterator(); iter.hasNext(); ) {
1445                 LogManager.LoggerWeakRef ref = iter.next();
1446                 if (ref == child) {
1447                     iter.remove();
1448                     return;
1449                 }
1450             }
1451         }
1452     }
1453 
1454     // Recalculate the effective level for this node and
1455     // recursively for our children.
1456 
1457     private void updateEffectiveLevel() {
1458         // assert Thread.holdsLock(treeLock);
1459 
1460         // Figure out our current effective level.
1461         int newLevelValue;
1462         if (levelObject != null) {
1463             newLevelValue = levelObject.intValue();
1464         } else {
1465             if (parent != null) {
1466                 newLevelValue = parent.levelValue;
1467             } else {
1468                 // This may happen during initialization.
1469                 newLevelValue = Level.INFO.intValue();
1470             }
1471         }
1472 
1473         // If our effective value hasn't changed, we're done.
1474         if (levelValue == newLevelValue) {
1475             return;
1476         }
1477 
1478         levelValue = newLevelValue;
1479 
1480         // System.err.println("effective level: \"" + getName() + "\" := " + level);
1481 
1482         // Recursively update the level on each of our kids.
1483         if (kids != null) {
1484             for (int i = 0; i < kids.size(); i++) {
1485                 LogManager.LoggerWeakRef ref = kids.get(i);
1486                 Logger kid =  ref.get();
1487                 if (kid != null) {
1488                     kid.updateEffectiveLevel();
1489                 }
1490             }
1491         }
1492     }
1493 
1494 
1495     // Private method to get the potentially inherited
1496     // resource bundle name for this Logger.
1497     // May return null
1498     private String getEffectiveResourceBundleName() {
1499         Logger target = this;
1500         while (target != null) {
1501             String rbn = target.getResourceBundleName();
1502             if (rbn != null) {
1503                 return rbn;
1504             }
1505             target = target.getParent();
1506         }
1507         return null;
1508     }
1509 
1510 
1511 }