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