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 gnu.java.lang.CPStringBuilder;
  42: 
  43: import java.util.List;
  44: import java.util.MissingResourceException;
  45: import java.util.ResourceBundle;
  46: import java.security.AccessController;
  47: import java.security.PrivilegedAction;
  48: 
  49: /**
  50:  * A Logger is used for logging information about events. Usually, there is a
  51:  * seprate logger for each subsystem or component, although there is a shared
  52:  * instance for components that make only occasional use of the logging
  53:  * framework.
  54:  * <p>
  55:  * It is common to name a logger after the name of a corresponding Java package.
  56:  * Loggers are organized into a hierarchical namespace; for example, the logger
  57:  * <code>"org.gnu.foo"</code> is the <em>parent</em> of logger
  58:  * <code>"org.gnu.foo.bar"</code>.
  59:  * <p>
  60:  * A logger for a named subsystem can be obtained through {@link
  61:  * java.util.logging.Logger#getLogger(java.lang.String)}. However, only code
  62:  * which has been granted the permission to control the logging infrastructure
  63:  * will be allowed to customize that logger. Untrusted code can obtain a
  64:  * private, anonymous logger through {@link #getAnonymousLogger()} if it wants
  65:  * to perform any modifications to the logger.
  66:  * <p>
  67:  * FIXME: Write more documentation.
  68:  * 
  69:  * @author Sascha Brawer (brawer@acm.org)
  70:  */
  71: public class Logger
  72: {
  73:   static final Logger root = new Logger("", null);
  74: 
  75:   /**
  76:    * A logger provided to applications that make only occasional use of the
  77:    * logging framework, typically early prototypes. Serious products are
  78:    * supposed to create and use their own Loggers, so they can be controlled
  79:    * individually.
  80:    */
  81:   public static final Logger global;
  82: 
  83:   /**
  84:    * Use to lock methods on this class instead of calling synchronize on methods
  85:    * to avoid deadlocks. Yeah, no kidding, we got them :)
  86:    */
  87:   private static final Object[] lock = new Object[0];
  88:   
  89:   static
  90:     {
  91:       // Our class might be initialized from an unprivileged context
  92:       global = (Logger) AccessController.doPrivileged(new PrivilegedAction()
  93:       {
  94:         public Object run()
  95:         {
  96:           return getLogger("global");
  97:         }
  98:       });
  99:     }
 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: }