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