001    /* Logger.java -- a class for logging messages
002       Copyright (C) 2002, 2004, 2006, 2007 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package java.util.logging;
040    
041    import java.util.List;
042    import java.util.MissingResourceException;
043    import java.util.ResourceBundle;
044    import java.security.AccessController;
045    import 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     */
070    public 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    }