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