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 }