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