001/* Logger.java -- a class for logging messages 002 Copyright (C) 2002, 2004, 2006, 2007 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package java.util.logging; 040 041import gnu.java.lang.CPStringBuilder; 042 043import java.util.List; 044import java.util.MissingResourceException; 045import java.util.ResourceBundle; 046import java.security.AccessController; 047import java.security.PrivilegedAction; 048 049/** 050 * A Logger is used for logging information about events. Usually, there is a 051 * seprate logger for each subsystem or component, although there is a shared 052 * instance for components that make only occasional use of the logging 053 * framework. 054 * <p> 055 * It is common to name a logger after the name of a corresponding Java package. 056 * Loggers are organized into a hierarchical namespace; for example, the logger 057 * <code>"org.gnu.foo"</code> is the <em>parent</em> of logger 058 * <code>"org.gnu.foo.bar"</code>. 059 * <p> 060 * A logger for a named subsystem can be obtained through {@link 061 * java.util.logging.Logger#getLogger(java.lang.String)}. However, only code 062 * which has been granted the permission to control the logging infrastructure 063 * will be allowed to customize that logger. Untrusted code can obtain a 064 * private, anonymous logger through {@link #getAnonymousLogger()} if it wants 065 * to perform any modifications to the logger. 066 * <p> 067 * FIXME: Write more documentation. 068 * 069 * @author Sascha Brawer (brawer@acm.org) 070 */ 071public class Logger 072{ 073 static final Logger root = new Logger("", null); 074 075 /** 076 * A logger provided to applications that make only occasional use of the 077 * logging framework, typically early prototypes. Serious products are 078 * supposed to create and use their own Loggers, so they can be controlled 079 * individually. 080 */ 081 public static final Logger global; 082 083 /** 084 * Use to lock methods on this class instead of calling synchronize on methods 085 * to avoid deadlocks. Yeah, no kidding, we got them :) 086 */ 087 private static final Object[] lock = new Object[0]; 088 089 static 090 { 091 // Our class might be initialized from an unprivileged context 092 global = (Logger) AccessController.doPrivileged(new PrivilegedAction() 093 { 094 public Object run() 095 { 096 return getLogger("global"); 097 } 098 }); 099 } 100 101 /** 102 * The name of the Logger, or <code>null</code> if the logger is anonymous. 103 * <p> 104 * A previous version of the GNU Classpath implementation granted untrusted 105 * code the permission to control any logger whose name was null. However, 106 * test code revealed that the Sun J2SE 1.4 reference implementation enforces 107 * the security control for any logger that was not created through 108 * getAnonymousLogger, even if it has a null name. Therefore, a separate flag 109 * {@link Logger#anonymous} was introduced. 110 */ 111 private final String name; 112 113 /** 114 * The name of the resource bundle used for localization. 115 * <p> 116 * This variable cannot be declared as <code>final</code> because its value 117 * can change as a result of calling getLogger(String,String). 118 */ 119 private String resourceBundleName; 120 121 /** 122 * The resource bundle used for localization. 123 * <p> 124 * This variable cannot be declared as <code>final</code> because its value 125 * can change as a result of calling getLogger(String,String). 126 */ 127 private ResourceBundle resourceBundle; 128 129 private Filter filter; 130 131 private final List handlerList = new java.util.ArrayList(4); 132 133 private Handler[] handlers = new Handler[0]; 134 135 /** 136 * Indicates whether or not this logger is anonymous. While a 137 * LoggingPermission is required for any modifications to a normal logger, 138 * untrusted code can obtain an anonymous logger and modify it according to 139 * its needs. 140 * <p> 141 * A previous version of the GNU Classpath implementation granted access to 142 * every logger whose name was null. However, test code revealed that the Sun 143 * J2SE 1.4 reference implementation enforces the security control for any 144 * logger that was not created through getAnonymousLogger, even if it has a 145 * null name. 146 */ 147 private boolean anonymous; 148 149 private boolean useParentHandlers; 150 151 private Level level; 152 153 private Logger parent; 154 155 /** 156 * Constructs a Logger for a subsystem. Most applications do not need to 157 * create new Loggers explicitly; instead, they should call the static factory 158 * methods {@link #getLogger(java.lang.String,java.lang.String) getLogger} 159 * (with ResourceBundle for localization) or 160 * {@link #getLogger(java.lang.String) getLogger} (without ResourceBundle), 161 * respectively. 162 * 163 * @param name the name for the logger, for example "java.awt" or 164 * "com.foo.bar". The name should be based on the name of the 165 * package issuing log records and consist of dot-separated Java 166 * identifiers. 167 * @param resourceBundleName the name of a resource bundle for localizing 168 * messages, or <code>null</code> to indicate that messages do 169 * not need to be localized. 170 * @throws java.util.MissingResourceException if 171 * <code>resourceBundleName</code> is not <code>null</code> 172 * and no such bundle could be located. 173 */ 174 protected Logger(String name, String resourceBundleName) 175 throws MissingResourceException 176 { 177 this.name = name; 178 this.resourceBundleName = resourceBundleName; 179 180 if (resourceBundleName == null) 181 resourceBundle = null; 182 else 183 resourceBundle = ResourceBundle.getBundle(resourceBundleName); 184 185 level = null; 186 187 /* 188 * This is null when the root logger is being constructed, and the root 189 * logger afterwards. 190 */ 191 parent = root; 192 193 useParentHandlers = (parent != null); 194 } 195 196 /** 197 * Finds a registered logger for a subsystem, or creates one in case no logger 198 * has been registered yet. 199 * 200 * @param name the name for the logger, for example "java.awt" or 201 * "com.foo.bar". The name should be based on the name of the 202 * package issuing log records and consist of dot-separated Java 203 * identifiers. 204 * @throws IllegalArgumentException if a logger for the subsystem identified 205 * by <code>name</code> has already been created, but uses a a 206 * resource bundle for localizing messages. 207 * @throws NullPointerException if <code>name</code> is <code>null</code>. 208 * @return a logger for the subsystem specified by <code>name</code> that 209 * does not localize messages. 210 */ 211 public static Logger getLogger(String name) 212 { 213 return getLogger(name, null); 214 } 215 216 /** 217 * Finds a registered logger for a subsystem, or creates one in case no logger 218 * has been registered yet. 219 * <p> 220 * If a logger with the specified name has already been registered, the 221 * behavior depends on the resource bundle that is currently associated with 222 * the existing logger. 223 * <ul> 224 * <li>If the existing logger uses the same resource bundle as specified by 225 * <code>resourceBundleName</code>, the existing logger is returned.</li> 226 * <li>If the existing logger currently does not localize messages, the 227 * existing logger is modified to use the bundle specified by 228 * <code>resourceBundleName</code>. The existing logger is then returned. 229 * Therefore, all subsystems currently using this logger will produce 230 * localized messages from now on.</li> 231 * <li>If the existing logger already has an associated resource bundle, but 232 * a different one than specified by <code>resourceBundleName</code>, an 233 * <code>IllegalArgumentException</code> is thrown.</li> 234 * </ul> 235 * 236 * @param name the name for the logger, for example "java.awt" or 237 * "org.gnu.foo". The name should be based on the name of the 238 * package issuing log records and consist of dot-separated Java 239 * identifiers. 240 * @param resourceBundleName the name of a resource bundle for localizing 241 * messages, or <code>null</code> to indicate that messages do 242 * not need to be localized. 243 * @return a logger for the subsystem specified by <code>name</code>. 244 * @throws java.util.MissingResourceException if 245 * <code>resourceBundleName</code> is not <code>null</code> 246 * and no such bundle could be located. 247 * @throws IllegalArgumentException if a logger for the subsystem identified 248 * by <code>name</code> has already been created, but uses a 249 * different resource bundle for localizing messages. 250 * @throws NullPointerException if <code>name</code> is <code>null</code>. 251 */ 252 public static Logger getLogger(String name, String resourceBundleName) 253 { 254 LogManager lm = LogManager.getLogManager(); 255 Logger result; 256 257 if (name == null) 258 throw new NullPointerException(); 259 260 /* 261 * Without synchronized(lm), it could happen that another thread would 262 * create a logger between our calls to getLogger and addLogger. While 263 * addLogger would indicate this by returning false, we could not be sure 264 * that this other logger was still existing when we called getLogger a 265 * second time in order to retrieve it -- note that LogManager is only 266 * allowed to keep weak references to registered loggers, so Loggers can be 267 * garbage collected at any time in general, and between our call to 268 * addLogger and our second call go getLogger in particular. Of course, we 269 * assume here that LogManager.addLogger etc. are synchronizing on the 270 * global LogManager object. There is a comment in the implementation of 271 * LogManager.addLogger referring to this comment here, so that any change 272 * in the synchronization of LogManager will be reflected here. 273 */ 274 synchronized (lock) 275 { 276 synchronized (lm) 277 { 278 result = lm.getLogger(name); 279 if (result == null) 280 { 281 boolean couldBeAdded; 282 283 result = new Logger(name, resourceBundleName); 284 couldBeAdded = lm.addLogger(result); 285 if (! couldBeAdded) 286 throw new IllegalStateException("cannot register new logger"); 287 } 288 else 289 { 290 /* 291 * The logger already exists. Make sure it uses the same 292 * resource bundle for localizing messages. 293 */ 294 String existingBundleName = result.getResourceBundleName(); 295 296 /* 297 * The Sun J2SE 1.4 reference implementation will return the 298 * registered logger object, even if it does not have a resource 299 * bundle associated with it. However, it seems to change the 300 * resourceBundle of the registered logger to the bundle whose 301 * name was passed to getLogger. 302 */ 303 if ((existingBundleName == null) && 304 (resourceBundleName != null)) 305 { 306 /* 307 * If ResourceBundle.getBundle throws an exception, the 308 * existing logger will be unchanged. This would be 309 * different if the assignment to resourceBundleName came 310 * first. 311 */ 312 result.resourceBundle = 313 ResourceBundle.getBundle(resourceBundleName); 314 315 result.resourceBundleName = resourceBundleName; 316 return result; 317 } 318 319 if ((existingBundleName != resourceBundleName) 320 && ((existingBundleName == null) 321 || !existingBundleName.equals(resourceBundleName))) 322 { 323 throw new IllegalArgumentException(); 324 } 325 } 326 } 327 } 328 329 return result; 330 } 331 332 /** 333 * Creates a new, unnamed logger. Unnamed loggers are not registered in the 334 * namespace of the LogManager, and no special security permission is required 335 * for changing their state. Therefore, untrusted applets are able to modify 336 * their private logger instance obtained through this method. 337 * <p> 338 * The parent of the newly created logger will the the root logger, from which 339 * the level threshold and the handlers are inherited. 340 */ 341 public static Logger getAnonymousLogger() 342 { 343 return getAnonymousLogger(null); 344 } 345 346 /** 347 * Creates a new, unnamed logger. Unnamed loggers are not registered in the 348 * namespace of the LogManager, and no special security permission is required 349 * for changing their state. Therefore, untrusted applets are able to modify 350 * their private logger instance obtained through this method. 351 * <p> 352 * The parent of the newly created logger will the the root logger, from which 353 * the level threshold and the handlers are inherited. 354 * 355 * @param resourceBundleName the name of a resource bundle for localizing 356 * messages, or <code>null</code> to indicate that messages do 357 * not need to be localized. 358 * @throws java.util.MissingResourceException if 359 * <code>resourceBundleName</code> is not <code>null</code> 360 * and no such bundle could be located. 361 */ 362 public static Logger getAnonymousLogger(String resourceBundleName) 363 throws MissingResourceException 364 { 365 Logger result; 366 367 result = new Logger(null, resourceBundleName); 368 result.anonymous = true; 369 return result; 370 } 371 372 /** 373 * Returns the name of the resource bundle that is being used for localizing 374 * messages. 375 * 376 * @return the name of the resource bundle used for localizing messages, or 377 * <code>null</code> if the parent's resource bundle is used for 378 * this purpose. 379 */ 380 public String getResourceBundleName() 381 { 382 synchronized (lock) 383 { 384 return resourceBundleName; 385 } 386 } 387 388 /** 389 * Returns the resource bundle that is being used for localizing messages. 390 * 391 * @return the resource bundle used for localizing messages, or 392 * <code>null</code> if the parent's resource bundle is used for 393 * this purpose. 394 */ 395 public ResourceBundle getResourceBundle() 396 { 397 synchronized (lock) 398 { 399 return resourceBundle; 400 } 401 } 402 403 /** 404 * Returns the severity level threshold for this <code>Handler</code>. All 405 * log records with a lower severity level will be discarded; a log record of 406 * the same or a higher level will be published unless an installed 407 * <code>Filter</code> decides to discard it. 408 * 409 * @return the severity level below which all log messages will be discarded, 410 * or <code>null</code> if the logger inherits the threshold from 411 * its parent. 412 */ 413 public Level getLevel() 414 { 415 synchronized (lock) 416 { 417 return level; 418 } 419 } 420 421 /** 422 * Returns whether or not a message of the specified level would be logged by 423 * this logger. 424 * 425 * @throws NullPointerException if <code>level</code> is <code>null</code>. 426 */ 427 public boolean isLoggable(Level level) 428 { 429 synchronized (lock) 430 { 431 if (this.level != null) 432 return this.level.intValue() <= level.intValue(); 433 434 if (parent != null) 435 return parent.isLoggable(level); 436 else 437 return false; 438 } 439 } 440 441 /** 442 * Sets the severity level threshold for this <code>Handler</code>. All log 443 * records with a lower severity level will be discarded immediately. A log 444 * record of the same or a higher level will be published unless an installed 445 * <code>Filter</code> decides to discard it. 446 * 447 * @param level the severity level below which all log messages will be 448 * discarded, or <code>null</code> to indicate that the logger 449 * should inherit the threshold from its parent. 450 * @throws SecurityException if this logger is not anonymous, a security 451 * manager exists, and the caller is not granted the permission to 452 * control the logging infrastructure by having 453 * LoggingPermission("control"). Untrusted code can obtain an 454 * anonymous logger through the static factory method 455 * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}. 456 */ 457 public void setLevel(Level level) 458 { 459 synchronized (lock) 460 { 461 /* 462 * An application is allowed to control an anonymous logger without 463 * having the permission to control the logging infrastructure. 464 */ 465 if (! anonymous) 466 LogManager.getLogManager().checkAccess(); 467 468 this.level = level; 469 } 470 } 471 472 public Filter getFilter() 473 { 474 synchronized (lock) 475 { 476 return filter; 477 } 478 } 479 480 /** 481 * @throws SecurityException if this logger is not anonymous, a security 482 * manager exists, and the caller is not granted the permission to 483 * control the logging infrastructure by having 484 * LoggingPermission("control"). Untrusted code can obtain an 485 * anonymous logger through the static factory method 486 * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}. 487 */ 488 public void setFilter(Filter filter) throws SecurityException 489 { 490 synchronized (lock) 491 { 492 /* 493 * An application is allowed to control an anonymous logger without 494 * having the permission to control the logging infrastructure. 495 */ 496 if (! anonymous) 497 LogManager.getLogManager().checkAccess(); 498 499 this.filter = filter; 500 } 501 } 502 503 /** 504 * Returns the name of this logger. 505 * 506 * @return the name of this logger, or <code>null</code> if the logger is 507 * anonymous. 508 */ 509 public String getName() 510 { 511 /* 512 * Note that the name of a logger cannot be changed during its lifetime, so 513 * no synchronization is needed. 514 */ 515 return name; 516 } 517 518 /** 519 * Passes a record to registered handlers, provided the record is considered 520 * as loggable both by {@link #isLoggable(Level)} and a possibly installed 521 * custom {@link #setFilter(Filter) filter}. 522 * <p> 523 * If the logger has been configured to use parent handlers, the record will 524 * be forwarded to the parent of this logger in addition to being processed by 525 * the handlers registered with this logger. 526 * <p> 527 * The other logging methods in this class are convenience methods that merely 528 * create a new LogRecord and pass it to this method. Therefore, subclasses 529 * usually just need to override this single method for customizing the 530 * logging behavior. 531 * 532 * @param record the log record to be inspected and possibly forwarded. 533 */ 534 public void log(LogRecord record) 535 { 536 synchronized (lock) 537 { 538 if (!isLoggable(record.getLevel())) 539 return; 540 541 if ((filter != null) && ! filter.isLoggable(record)) 542 return; 543 544 /* 545 * If no logger name has been set for the log record, use the name of 546 * this logger. 547 */ 548 if (record.getLoggerName() == null) 549 record.setLoggerName(name); 550 551 /* 552 * Avoid that some other thread is changing the logger hierarchy while 553 * we are traversing it. 554 */ 555 synchronized (LogManager.getLogManager()) 556 { 557 Logger curLogger = this; 558 559 do 560 { 561 /* 562 * The Sun J2SE 1.4 reference implementation seems to call the 563 * filter only for the logger whose log method is called, never 564 * for any of its parents. Also, parent loggers publish log 565 * record whatever their level might be. This is pretty weird, 566 * but GNU Classpath tries to be as compatible as possible to 567 * the reference implementation. 568 */ 569 for (int i = 0; i < curLogger.handlers.length; i++) 570 curLogger.handlers[i].publish(record); 571 572 if (curLogger.getUseParentHandlers() == false) 573 break; 574 575 curLogger = curLogger.getParent(); 576 } 577 while (parent != null); 578 } 579 } 580 } 581 582 public void log(Level level, String message) 583 { 584 if (isLoggable(level)) 585 log(level, message, (Object[]) null); 586 } 587 588 public void log(Level level, String message, Object param) 589 { 590 synchronized (lock) 591 { 592 if (isLoggable(level)) 593 { 594 StackTraceElement caller = getCallerStackFrame(); 595 logp(level, caller != null ? caller.getClassName() : "<unknown>", 596 caller != null ? caller.getMethodName() : "<unknown>", 597 message, param); 598 } 599 } 600 } 601 602 public void log(Level level, String message, Object[] params) 603 { 604 synchronized (lock) 605 { 606 if (isLoggable(level)) 607 { 608 StackTraceElement caller = getCallerStackFrame(); 609 logp(level, caller != null ? caller.getClassName() : "<unknown>", 610 caller != null ? caller.getMethodName() : "<unknown>", 611 message, params); 612 613 } 614 } 615 } 616 617 public void log(Level level, String message, Throwable thrown) 618 { 619 synchronized (lock) 620 { 621 if (isLoggable(level)) 622 { 623 StackTraceElement caller = getCallerStackFrame(); 624 logp(level, caller != null ? caller.getClassName() : "<unknown>", 625 caller != null ? caller.getMethodName() : "<unknown>", 626 message, thrown); 627 } 628 } 629 } 630 631 public void logp(Level level, String sourceClass, String sourceMethod, 632 String message) 633 { 634 synchronized (lock) 635 { 636 logp(level, sourceClass, sourceMethod, message, (Object[]) null); 637 } 638 } 639 640 public void logp(Level level, String sourceClass, String sourceMethod, 641 String message, Object param) 642 { 643 synchronized (lock) 644 { 645 logp(level, sourceClass, sourceMethod, message, new Object[] { param }); 646 } 647 648 } 649 650 private ResourceBundle findResourceBundle() 651 { 652 synchronized (lock) 653 { 654 if (resourceBundle != null) 655 return resourceBundle; 656 657 if (parent != null) 658 return parent.findResourceBundle(); 659 660 return null; 661 } 662 } 663 664 private void logImpl(Level level, String sourceClass, String sourceMethod, 665 String message, Object[] params) 666 { 667 synchronized (lock) 668 { 669 LogRecord rec = new LogRecord(level, message); 670 671 rec.setResourceBundle(findResourceBundle()); 672 rec.setSourceClassName(sourceClass); 673 rec.setSourceMethodName(sourceMethod); 674 rec.setParameters(params); 675 676 log(rec); 677 } 678 } 679 680 public void logp(Level level, String sourceClass, String sourceMethod, 681 String message, Object[] params) 682 { 683 synchronized (lock) 684 { 685 logImpl(level, sourceClass, sourceMethod, message, params); 686 } 687 } 688 689 public void logp(Level level, String sourceClass, String sourceMethod, 690 String message, Throwable thrown) 691 { 692 synchronized (lock) 693 { 694 LogRecord rec = new LogRecord(level, message); 695 696 rec.setResourceBundle(resourceBundle); 697 rec.setSourceClassName(sourceClass); 698 rec.setSourceMethodName(sourceMethod); 699 rec.setThrown(thrown); 700 701 log(rec); 702 } 703 } 704 705 public void logrb(Level level, String sourceClass, String sourceMethod, 706 String bundleName, String message) 707 { 708 synchronized (lock) 709 { 710 logrb(level, sourceClass, sourceMethod, bundleName, message, 711 (Object[]) null); 712 } 713 } 714 715 public void logrb(Level level, String sourceClass, String sourceMethod, 716 String bundleName, String message, Object param) 717 { 718 synchronized (lock) 719 { 720 logrb(level, sourceClass, sourceMethod, bundleName, message, 721 new Object[] { param }); 722 } 723 } 724 725 public void logrb(Level level, String sourceClass, String sourceMethod, 726 String bundleName, String message, Object[] params) 727 { 728 synchronized (lock) 729 { 730 LogRecord rec = new LogRecord(level, message); 731 732 rec.setResourceBundleName(bundleName); 733 rec.setSourceClassName(sourceClass); 734 rec.setSourceMethodName(sourceMethod); 735 rec.setParameters(params); 736 737 log(rec); 738 } 739 } 740 741 public void logrb(Level level, String sourceClass, String sourceMethod, 742 String bundleName, String message, Throwable thrown) 743 { 744 synchronized (lock) 745 { 746 LogRecord rec = new LogRecord(level, message); 747 748 rec.setResourceBundleName(bundleName); 749 rec.setSourceClassName(sourceClass); 750 rec.setSourceMethodName(sourceMethod); 751 rec.setThrown(thrown); 752 753 log(rec); 754 } 755 } 756 757 public void entering(String sourceClass, String sourceMethod) 758 { 759 synchronized (lock) 760 { 761 if (isLoggable(Level.FINER)) 762 logp(Level.FINER, sourceClass, sourceMethod, "ENTRY"); 763 } 764 } 765 766 public void entering(String sourceClass, String sourceMethod, Object param) 767 { 768 synchronized (lock) 769 { 770 if (isLoggable(Level.FINER)) 771 logp(Level.FINER, sourceClass, sourceMethod, "ENTRY {0}", param); 772 } 773 } 774 775 public void entering(String sourceClass, String sourceMethod, Object[] params) 776 { 777 synchronized (lock) 778 { 779 if (isLoggable(Level.FINER)) 780 { 781 CPStringBuilder buf = new CPStringBuilder(80); 782 buf.append("ENTRY"); 783 for (int i = 0; i < params.length; i++) 784 { 785 buf.append(" {"); 786 buf.append(i); 787 buf.append('}'); 788 } 789 790 logp(Level.FINER, sourceClass, sourceMethod, buf.toString(), params); 791 } 792 } 793 } 794 795 public void exiting(String sourceClass, String sourceMethod) 796 { 797 synchronized (lock) 798 { 799 if (isLoggable(Level.FINER)) 800 logp(Level.FINER, sourceClass, sourceMethod, "RETURN"); 801 } 802 } 803 804 public void exiting(String sourceClass, String sourceMethod, Object result) 805 { 806 synchronized (lock) 807 { 808 if (isLoggable(Level.FINER)) 809 logp(Level.FINER, sourceClass, sourceMethod, "RETURN {0}", result); 810 } 811 } 812 813 public void throwing(String sourceClass, String sourceMethod, Throwable thrown) 814 { 815 synchronized (lock) 816 { 817 if (isLoggable(Level.FINER)) 818 logp(Level.FINER, sourceClass, sourceMethod, "THROW", thrown); 819 } 820 } 821 822 /** 823 * Logs a message with severity level SEVERE, indicating a serious failure 824 * that prevents normal program execution. Messages at this level should be 825 * understandable to an inexperienced, non-technical end user. Ideally, they 826 * explain in simple words what actions the user can take in order to resolve 827 * the problem. 828 * 829 * @see Level#SEVERE 830 * @param message the message text, also used as look-up key if the logger is 831 * localizing messages with a resource bundle. While it is possible 832 * to pass <code>null</code>, this is not recommended, since a 833 * logging message without text is unlikely to be helpful. 834 */ 835 public void severe(String message) 836 { 837 synchronized (lock) 838 { 839 if (isLoggable(Level.SEVERE)) 840 log(Level.SEVERE, message); 841 } 842 } 843 844 /** 845 * Logs a message with severity level WARNING, indicating a potential problem 846 * that does not prevent normal program execution. Messages at this level 847 * should be understandable to an inexperienced, non-technical end user. 848 * Ideally, they explain in simple words what actions the user can take in 849 * order to resolve the problem. 850 * 851 * @see Level#WARNING 852 * @param message the message text, also used as look-up key if the logger is 853 * localizing messages with a resource bundle. While it is possible 854 * to pass <code>null</code>, this is not recommended, since a 855 * logging message without text is unlikely to be helpful. 856 */ 857 public void warning(String message) 858 { 859 synchronized (lock) 860 { 861 if (isLoggable(Level.WARNING)) 862 log(Level.WARNING, message); 863 } 864 } 865 866 /** 867 * Logs a message with severity level INFO. {@link Level#INFO} is intended for 868 * purely informational messages that do not indicate error or warning 869 * situations. In the default logging configuration, INFO messages will be 870 * written to the system console. For this reason, the INFO level should be 871 * used only for messages that are important to end users and system 872 * administrators. Messages at this level should be understandable to an 873 * inexperienced, non-technical user. 874 * 875 * @param message the message text, also used as look-up key if the logger is 876 * localizing messages with a resource bundle. While it is possible 877 * to pass <code>null</code>, this is not recommended, since a 878 * logging message without text is unlikely to be helpful. 879 */ 880 public void info(String message) 881 { 882 synchronized (lock) 883 { 884 if (isLoggable(Level.INFO)) 885 log(Level.INFO, message); 886 } 887 } 888 889 /** 890 * Logs a message with severity level CONFIG. {@link Level#CONFIG} is intended 891 * for static configuration messages, for example about the windowing 892 * environment, the operating system version, etc. 893 * 894 * @param message the message text, also used as look-up key if the logger is 895 * localizing messages with a resource bundle. While it is possible 896 * to pass <code>null</code>, this is not recommended, since a 897 * logging message without text is unlikely to be helpful. 898 */ 899 public void config(String message) 900 { 901 synchronized (lock) 902 { 903 if (isLoggable(Level.CONFIG)) 904 log(Level.CONFIG, message); 905 } 906 } 907 908 /** 909 * Logs a message with severity level FINE. {@link Level#FINE} is intended for 910 * messages that are relevant for developers using the component generating 911 * log messages. Examples include minor, recoverable failures, or possible 912 * inefficiencies. 913 * 914 * @param message the message text, also used as look-up key if the logger is 915 * localizing messages with a resource bundle. While it is possible 916 * to pass <code>null</code>, this is not recommended, since a 917 * logging message without text is unlikely to be helpful. 918 */ 919 public void fine(String message) 920 { 921 synchronized (lock) 922 { 923 if (isLoggable(Level.FINE)) 924 log(Level.FINE, message); 925 } 926 } 927 928 /** 929 * Logs a message with severity level FINER. {@link Level#FINER} is intended 930 * for rather detailed tracing, for example entering a method, returning from 931 * a method, or throwing an exception. 932 * 933 * @param message the message text, also used as look-up key if the logger is 934 * localizing messages with a resource bundle. While it is possible 935 * to pass <code>null</code>, this is not recommended, since a 936 * logging message without text is unlikely to be helpful. 937 */ 938 public void finer(String message) 939 { 940 synchronized (lock) 941 { 942 if (isLoggable(Level.FINER)) 943 log(Level.FINER, message); 944 } 945 } 946 947 /** 948 * Logs a message with severity level FINEST. {@link Level#FINEST} is intended 949 * for highly detailed tracing, for example reaching a certain point inside 950 * the body of a method. 951 * 952 * @param message the message text, also used as look-up key if the logger is 953 * localizing messages with a resource bundle. While it is possible 954 * to pass <code>null</code>, this is not recommended, since a 955 * logging message without text is unlikely to be helpful. 956 */ 957 public void finest(String message) 958 { 959 synchronized (lock) 960 { 961 if (isLoggable(Level.FINEST)) 962 log(Level.FINEST, message); 963 } 964 } 965 966 /** 967 * Adds a handler to the set of handlers that get notified when a log record 968 * is to be published. 969 * 970 * @param handler the handler to be added. 971 * @throws NullPointerException if <code>handler</code> is <code>null</code>. 972 * @throws SecurityException if this logger is not anonymous, a security 973 * manager exists, and the caller is not granted the permission to 974 * control the logging infrastructure by having 975 * LoggingPermission("control"). Untrusted code can obtain an 976 * anonymous logger through the static factory method 977 * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}. 978 */ 979 public void addHandler(Handler handler) throws SecurityException 980 { 981 synchronized (lock) 982 { 983 if (handler == null) 984 throw new NullPointerException(); 985 986 /* 987 * An application is allowed to control an anonymous logger without 988 * having the permission to control the logging infrastructure. 989 */ 990 if (! anonymous) 991 LogManager.getLogManager().checkAccess(); 992 993 if (! handlerList.contains(handler)) 994 { 995 handlerList.add(handler); 996 handlers = getHandlers(); 997 } 998 } 999 } 1000 1001 /** 1002 * Removes a handler from the set of handlers that get notified when a log 1003 * record is to be published. 1004 * 1005 * @param handler the handler to be removed. 1006 * @throws SecurityException if this logger is not anonymous, a security 1007 * manager exists, and the caller is not granted the permission to 1008 * control the logging infrastructure by having 1009 * LoggingPermission("control"). Untrusted code can obtain an 1010 * anonymous logger through the static factory method {@link 1011 * #getAnonymousLogger(java.lang.String) getAnonymousLogger}. 1012 * @throws NullPointerException if <code>handler</code> is <code>null</code>. 1013 */ 1014 public void removeHandler(Handler handler) throws SecurityException 1015 { 1016 synchronized (lock) 1017 { 1018 /* 1019 * An application is allowed to control an anonymous logger without 1020 * having the permission to control the logging infrastructure. 1021 */ 1022 if (! anonymous) 1023 LogManager.getLogManager().checkAccess(); 1024 1025 if (handler == null) 1026 throw new NullPointerException(); 1027 1028 handlerList.remove(handler); 1029 handlers = getHandlers(); 1030 } 1031 } 1032 1033 /** 1034 * Returns the handlers currently registered for this Logger. When a log 1035 * record has been deemed as being loggable, it will be passed to all 1036 * registered handlers for publication. In addition, if the logger uses parent 1037 * handlers (see {@link #getUseParentHandlers() getUseParentHandlers} and 1038 * {@link #setUseParentHandlers(boolean) setUseParentHandlers}, the log 1039 * record will be passed to the parent's handlers. 1040 */ 1041 public Handler[] getHandlers() 1042 { 1043 synchronized (lock) 1044 { 1045 /* 1046 * We cannot return our internal handlers array because we do not have 1047 * any guarantee that the caller would not change the array entries. 1048 */ 1049 return (Handler[]) handlerList.toArray(new Handler[handlerList.size()]); 1050 } 1051 } 1052 1053 /** 1054 * Returns whether or not this Logger forwards log records to handlers 1055 * registered for its parent loggers. 1056 * 1057 * @return <code>false</code> if this Logger sends log records merely to 1058 * Handlers registered with itself; <code>true</code> if this Logger 1059 * sends log records not only to Handlers registered with itself, but 1060 * also to those Handlers registered with parent loggers. 1061 */ 1062 public boolean getUseParentHandlers() 1063 { 1064 synchronized (lock) 1065 { 1066 return useParentHandlers; 1067 } 1068 } 1069 1070 /** 1071 * Sets whether or not this Logger forwards log records to handlers registered 1072 * for its parent loggers. 1073 * 1074 * @param useParentHandlers <code>false</code> to let this Logger send log 1075 * records merely to Handlers registered with itself; 1076 * <code>true</code> to let this Logger send log records not only 1077 * to Handlers registered with itself, but also to those Handlers 1078 * registered with parent loggers. 1079 * @throws SecurityException if this logger is not anonymous, a security 1080 * manager exists, and the caller is not granted the permission to 1081 * control the logging infrastructure by having 1082 * LoggingPermission("control"). Untrusted code can obtain an 1083 * anonymous logger through the static factory method 1084 * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}. 1085 */ 1086 public void setUseParentHandlers(boolean useParentHandlers) 1087 { 1088 synchronized (lock) 1089 { 1090 /* 1091 * An application is allowed to control an anonymous logger without 1092 * having the permission to control the logging infrastructure. 1093 */ 1094 if (! anonymous) 1095 LogManager.getLogManager().checkAccess(); 1096 1097 this.useParentHandlers = useParentHandlers; 1098 } 1099 } 1100 1101 /** 1102 * Returns the parent of this logger. By default, the parent is assigned by 1103 * the LogManager by inspecting the logger's name. 1104 * 1105 * @return the parent of this logger (as detemined by the LogManager by 1106 * inspecting logger names), the root logger if no other logger has a 1107 * name which is a prefix of this logger's name, or <code>null</code> 1108 * for the root logger. 1109 */ 1110 public Logger getParent() 1111 { 1112 synchronized (lock) 1113 { 1114 return parent; 1115 } 1116 } 1117 1118 /** 1119 * Sets the parent of this logger. Usually, applications do not call this 1120 * method directly. Instead, the LogManager will ensure that the tree of 1121 * loggers reflects the hierarchical logger namespace. Basically, this method 1122 * should not be public at all, but the GNU implementation follows the API 1123 * specification. 1124 * 1125 * @throws NullPointerException if <code>parent</code> is <code>null</code>. 1126 * @throws SecurityException if this logger is not anonymous, a security 1127 * manager exists, and the caller is not granted the permission to 1128 * control the logging infrastructure by having 1129 * LoggingPermission("control"). Untrusted code can obtain an 1130 * anonymous logger through the static factory method 1131 * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}. 1132 */ 1133 public void setParent(Logger parent) 1134 { 1135 synchronized (lock) 1136 { 1137 if (parent == null) 1138 throw new NullPointerException(); 1139 1140 if (this == root) 1141 throw new IllegalArgumentException( 1142 "the root logger can only have a null parent"); 1143 1144 /* 1145 * An application is allowed to control an anonymous logger without 1146 * having the permission to control the logging infrastructure. 1147 */ 1148 if (! anonymous) 1149 LogManager.getLogManager().checkAccess(); 1150 1151 this.parent = parent; 1152 } 1153 } 1154 1155 /** 1156 * Gets the StackTraceElement of the first class that is not this class. That 1157 * should be the initial caller of a logging method. 1158 * 1159 * @return caller of the initial logging method or null if unknown. 1160 */ 1161 private StackTraceElement getCallerStackFrame() 1162 { 1163 Throwable t = new Throwable(); 1164 StackTraceElement[] stackTrace = t.getStackTrace(); 1165 int index = 0; 1166 1167 // skip to stackentries until this class 1168 while (index < stackTrace.length 1169 && ! stackTrace[index].getClassName().equals(getClass().getName())) 1170 index++; 1171 1172 // skip the stackentries of this class 1173 while (index < stackTrace.length 1174 && stackTrace[index].getClassName().equals(getClass().getName())) 1175 index++; 1176 1177 return index < stackTrace.length ? stackTrace[index] : null; 1178 } 1179 1180 /** 1181 * Reset and close handlers attached to this logger. This function is package 1182 * private because it must only be available to the LogManager. 1183 */ 1184 void resetLogger() 1185 { 1186 for (int i = 0; i < handlers.length; i++) 1187 { 1188 handlers[i].close(); 1189 handlerList.remove(handlers[i]); 1190 } 1191 handlers = getHandlers(); 1192 } 1193}