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