Source for java.util.logging.Logger

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