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