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