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