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