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