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