View Javadoc

1   /*
2    * Copyright 2001-2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.commons.logging.impl;
18  
19  import java.io.InputStream;
20  import java.io.Serializable;
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Method;
23  import java.security.AccessController;
24  import java.security.PrivilegedAction;
25  import java.text.DateFormat;
26  import java.text.SimpleDateFormat;
27  import java.util.Date;
28  import java.util.Properties;
29  
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogConfigurationException;
32  
33  /**
34   * <p>
35   * Simple implementation of Log that sends all enabled log messages, for all
36   * defined loggers, to System.err. The following system properties are supported
37   * to configure the behavior of this logger:
38   * </p>
39   * <ul>
40   * <li><code>org.apache.commons.logging.simplelog.defaultlog</code> - Default
41   * logging detail level for all instances of SimpleLog. Must be one of ("trace",
42   * "debug", "info", "warn", "error", or "fatal"). If not specified, defaults to
43   * "info".</li>
44   * <li><code>org.apache.commons.logging.simplelog.log.xxxxx</code> - Logging
45   * detail level for a SimpleLog instance named "xxxxx". Must be one of ("trace",
46   * "debug", "info", "warn", "error", or "fatal"). If not specified, the default
47   * logging detail level is used.</li>
48   * <li><code>org.apache.commons.logging.simplelog.showlogname</code> - Set to
49   * <code>true</code> if you want the Log instance name to be included in output
50   * messages. Defaults to <code>false</code>.</li>
51   * <li><code>org.apache.commons.logging.simplelog.showShortLogname</code> - Set
52   * to <code>true</code> if you want the last component of the name to be
53   * included in output messages. Defaults to <code>true</code>.</li>
54   * <li><code>org.apache.commons.logging.simplelog.showdatetime</code> - Set to
55   * <code>true</code> if you want the current date and time to be included in
56   * output messages. Default is <code>false</code>.</li>
57   * <li><code>org.apache.commons.logging.simplelog.dateTimeFormat</code> - The
58   * date and time format to be used in the output messages. The pattern
59   * describing the date and time format is the same that is used in
60   * <code>java.text.SimpleDateFormat</code>. If the format is not specified or is
61   * invalid, the default format is used. The default format is
62   * <code>yyyy/MM/dd HH:mm:ss:SSS zzz</code>.</li>
63   * </ul>
64   * 
65   * <p>
66   * In addition to looking for system properties with the names specified above,
67   * this implementation also checks for a class loader resource named
68   * <code>"simplelog.properties"</code>, and includes any matching definitions
69   * from this resource (if it exists).
70   * </p>
71   * 
72   * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
73   * @author Rod Waldhoff
74   * @author Robert Burrell Donkin
75   * 
76   * @version $Id: SimpleLog.java,v 1.21 2004/06/06 20:47:56 rdonkin Exp $
77   */
78  public class SimpleLog implements Log, Serializable {
79  
80      private static final long serialVersionUID = 136942970684951178L;
81  
82      // ------------------------------------------------------- Class Attributes
83  
84      /** All system properties used by <code>SimpleLog</code> start with this */
85      static protected final String systemPrefix = "org.apache.commons.logging.simplelog.";
86  
87      /** Properties loaded from simplelog.properties */
88      static protected final Properties simpleLogProps = new Properties();
89  
90      /** The default format to use when formating dates */
91      static protected final String DEFAULT_DATE_TIME_FORMAT = "yyyy/MM/dd HH:mm:ss:SSS zzz";
92  
93      /** Include the instance name in the log message? */
94      static protected boolean showLogName = false;
95      /**
96       * Include the short name ( last component ) of the logger in the log message.
97       * Defaults to true - otherwise we'll be lost in a flood of messages without
98       * knowing who sends them.
99       */
100     static protected boolean showShortName = true;
101     /** Include the current time in the log message */
102     static protected boolean showDateTime = false;
103     /** The date and time format to use in the log message */
104     static protected String dateTimeFormat = DEFAULT_DATE_TIME_FORMAT;
105     /** Used to format times */
106     static protected DateFormat dateFormatter = null;
107 
108     // ---------------------------------------------------- Log Level Constants
109 
110     /** "Trace" level logging. */
111     public static final int LOG_LEVEL_TRACE = 1;
112     /** "Debug" level logging. */
113     public static final int LOG_LEVEL_DEBUG = 2;
114     /** "Info" level logging. */
115     public static final int LOG_LEVEL_INFO = 3;
116     /** "Warn" level logging. */
117     public static final int LOG_LEVEL_WARN = 4;
118     /** "Error" level logging. */
119     public static final int LOG_LEVEL_ERROR = 5;
120     /** "Fatal" level logging. */
121     public static final int LOG_LEVEL_FATAL = 6;
122 
123     /** Enable all logging levels */
124     public static final int LOG_LEVEL_ALL = (LOG_LEVEL_TRACE - 1);
125 
126     /** Enable no logging levels */
127     public static final int LOG_LEVEL_OFF = (LOG_LEVEL_FATAL + 1);
128 
129     // ------------------------------------------------------------ Initializer
130 
131     private static String getStringProperty(String name) {
132         String prop = null;
133         try {
134             prop = System.getProperty(name);
135         } catch (SecurityException e) {
136             ; // Ignore
137         }
138         return (prop == null) ? simpleLogProps.getProperty(name) : prop;
139     }
140 
141     private static String getStringProperty(String name, String dephault) {
142         String prop = getStringProperty(name);
143         return (prop == null) ? dephault : prop;
144     }
145 
146     private static boolean getBooleanProperty(String name, boolean dephault) {
147         String prop = getStringProperty(name);
148         return (prop == null) ? dephault : "true".equalsIgnoreCase(prop);
149     }
150 
151     // Initialize class attributes.
152     // Load properties file, if found.
153     // Override with system properties.
154     static {
155         // Add props from the resource simplelog.properties
156         InputStream in = getResourceAsStream("simplelog.properties");
157         if (null != in) {
158             try {
159                 simpleLogProps.load(in);
160                 in.close();
161             } catch (java.io.IOException e) {
162                 // ignored
163             }
164         }
165 
166         showLogName = getBooleanProperty(systemPrefix + "showlogname", showLogName);
167         showShortName = getBooleanProperty(systemPrefix + "showShortLogname", showShortName);
168         showDateTime = getBooleanProperty(systemPrefix + "showdatetime", showDateTime);
169 
170         if (showDateTime) {
171             dateTimeFormat = getStringProperty(systemPrefix + "dateTimeFormat", dateTimeFormat);
172             try {
173                 dateFormatter = new SimpleDateFormat(dateTimeFormat);
174             } catch (IllegalArgumentException e) {
175                 // If the format pattern is invalid - use the default format
176                 dateTimeFormat = DEFAULT_DATE_TIME_FORMAT;
177                 dateFormatter = new SimpleDateFormat(dateTimeFormat);
178             }
179         }
180     }
181 
182     // ------------------------------------------------------------- Attributes
183 
184     /** The name of this simple log instance */
185     protected String logName = null;
186     /** The current log level */
187     protected int currentLogLevel;
188     /** The short name of this simple log instance */
189     private String shortLogName = null;
190 
191     // ------------------------------------------------------------ Constructor
192 
193     /**
194      * Construct a simple log with given name.
195      * 
196      * @param name
197      *          log name
198      */
199     public SimpleLog(String name) {
200 
201         logName = name;
202 
203         // Set initial log level
204         // Used to be: set default log level to ERROR
205         // IMHO it should be lower, but at least info ( costin ).
206         setLevel(SimpleLog.LOG_LEVEL_INFO);
207 
208         // Set log level from properties
209         String lvl = getStringProperty(systemPrefix + "log." + logName);
210         int i = String.valueOf(name).lastIndexOf(".");
211         while (null == lvl && i > -1) {
212             name = name.substring(0, i);
213             lvl = getStringProperty(systemPrefix + "log." + name);
214             i = String.valueOf(name).lastIndexOf(".");
215         }
216 
217         if (null == lvl) {
218             lvl = getStringProperty(systemPrefix + "defaultlog");
219         }
220 
221         if ("all".equalsIgnoreCase(lvl)) {
222             setLevel(SimpleLog.LOG_LEVEL_ALL);
223         } else if ("trace".equalsIgnoreCase(lvl)) {
224             setLevel(SimpleLog.LOG_LEVEL_TRACE);
225         } else if ("debug".equalsIgnoreCase(lvl)) {
226             setLevel(SimpleLog.LOG_LEVEL_DEBUG);
227         } else if ("info".equalsIgnoreCase(lvl)) {
228             setLevel(SimpleLog.LOG_LEVEL_INFO);
229         } else if ("warn".equalsIgnoreCase(lvl)) {
230             setLevel(SimpleLog.LOG_LEVEL_WARN);
231         } else if ("error".equalsIgnoreCase(lvl)) {
232             setLevel(SimpleLog.LOG_LEVEL_ERROR);
233         } else if ("fatal".equalsIgnoreCase(lvl)) {
234             setLevel(SimpleLog.LOG_LEVEL_FATAL);
235         } else if ("off".equalsIgnoreCase(lvl)) {
236             setLevel(SimpleLog.LOG_LEVEL_OFF);
237         }
238 
239     }
240 
241     // -------------------------------------------------------- Properties
242 
243     /**
244      * <p>
245      * Set logging level.
246      * </p>
247      * 
248      * @param currentLogLevel
249      *          new logging level
250      */
251     public void setLevel(int currentLogLevel) {
252 
253         this.currentLogLevel = currentLogLevel;
254 
255     }
256 
257     /**
258      * <p>
259      * Get logging level.
260      * </p>
261      */
262     public int getLevel() {
263 
264         return currentLogLevel;
265     }
266 
267     // -------------------------------------------------------- Logging Methods
268 
269     /**
270      * <p>
271      * Do the actual logging. This method assembles the message and then calls
272      * <code>write()</code> to cause it to be written.
273      * </p>
274      * 
275      * @param type
276      *          One of the LOG_LEVEL_XXX constants defining the log level
277      * @param message
278      *          The message itself (typically a String)
279      * @param t
280      *          The exception whose stack trace should be logged
281      */
282     protected void log(int type, Object message, Throwable t) {
283         // Use a string buffer for better performance
284         StringBuffer buf = new StringBuffer();
285 
286         // Append date-time if so configured
287         if (showDateTime) {
288             buf.append(dateFormatter.format(new Date()));
289             buf.append(" ");
290         }
291 
292         // Append a readable representation of the log level
293         switch (type) {
294         case SimpleLog.LOG_LEVEL_TRACE:
295             buf.append("[TRACE] ");
296             break;
297         case SimpleLog.LOG_LEVEL_DEBUG:
298             buf.append("[DEBUG] ");
299             break;
300         case SimpleLog.LOG_LEVEL_INFO:
301             buf.append("[INFO] ");
302             break;
303         case SimpleLog.LOG_LEVEL_WARN:
304             buf.append("[WARN] ");
305             break;
306         case SimpleLog.LOG_LEVEL_ERROR:
307             buf.append("[ERROR] ");
308             break;
309         case SimpleLog.LOG_LEVEL_FATAL:
310             buf.append("[FATAL] ");
311             break;
312         }
313 
314         // Append the name of the log instance if so configured
315         if (showShortName) {
316             if (shortLogName == null) {
317                 // Cut all but the last component of the name for both styles
318                 shortLogName = logName.substring(logName.lastIndexOf(".") + 1);
319                 shortLogName = shortLogName.substring(shortLogName.lastIndexOf("/") + 1);
320             }
321             buf.append(String.valueOf(shortLogName)).append(" - ");
322         } else if (showLogName) {
323             buf.append(String.valueOf(logName)).append(" - ");
324         }
325 
326         // Append the message
327         buf.append(String.valueOf(message));
328 
329         // Append stack trace if not null
330         if (t != null) {
331             buf.append(" <");
332             buf.append(t.toString());
333             buf.append(">");
334 
335             java.io.StringWriter sw = new java.io.StringWriter(1024);
336             java.io.PrintWriter pw = new java.io.PrintWriter(sw);
337             t.printStackTrace(pw);
338             pw.close();
339             buf.append(sw.toString());
340         }
341 
342         // Print to the appropriate destination
343         write(buf);
344 
345     }
346 
347     /**
348      * <p>
349      * Write the content of the message accumulated in the specified
350      * <code>StringBuffer</code> to the appropriate output destination. The
351      * default implementation writes to <code>System.err</code>.
352      * </p>
353      * 
354      * @param buffer
355      *          A <code>StringBuffer</code> containing the accumulated text to be
356      *          logged
357      */
358     protected void write(StringBuffer buffer) {
359 
360         System.err.println(buffer.toString());
361 
362     }
363 
364     /**
365      * Is the given log level currently enabled?
366      * 
367      * @param logLevel
368      *          is this level enabled?
369      */
370     protected boolean isLevelEnabled(int logLevel) {
371         // log level are numerically ordered so can use simple numeric
372         // comparison
373         return (logLevel >= currentLogLevel);
374     }
375 
376     // -------------------------------------------------------- Log Implementation
377 
378     /**
379      * <p>
380      * Log a message with debug log level.
381      * </p>
382      */
383     public final void debug(Object message) {
384 
385         if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) {
386             log(SimpleLog.LOG_LEVEL_DEBUG, message, null);
387         }
388     }
389 
390     /**
391      * <p>
392      * Log an error with debug log level.
393      * </p>
394      */
395     public final void debug(Object message, Throwable t) {
396 
397         if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) {
398             log(SimpleLog.LOG_LEVEL_DEBUG, message, t);
399         }
400     }
401 
402     /**
403      * <p>
404      * Log a message with trace log level.
405      * </p>
406      */
407     public final void trace(Object message) {
408 
409         if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) {
410             log(SimpleLog.LOG_LEVEL_TRACE, message, null);
411         }
412     }
413 
414     /**
415      * <p>
416      * Log an error with trace log level.
417      * </p>
418      */
419     public final void trace(Object message, Throwable t) {
420 
421         if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) {
422             log(SimpleLog.LOG_LEVEL_TRACE, message, t);
423         }
424     }
425 
426     /**
427      * <p>
428      * Log a message with info log level.
429      * </p>
430      */
431     public final void info(Object message) {
432 
433         if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) {
434             log(SimpleLog.LOG_LEVEL_INFO, message, null);
435         }
436     }
437 
438     /**
439      * <p>
440      * Log an error with info log level.
441      * </p>
442      */
443     public final void info(Object message, Throwable t) {
444 
445         if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) {
446             log(SimpleLog.LOG_LEVEL_INFO, message, t);
447         }
448     }
449 
450     /**
451      * <p>
452      * Log a message with warn log level.
453      * </p>
454      */
455     public final void warn(Object message) {
456 
457         if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) {
458             log(SimpleLog.LOG_LEVEL_WARN, message, null);
459         }
460     }
461 
462     /**
463      * <p>
464      * Log an error with warn log level.
465      * </p>
466      */
467     public final void warn(Object message, Throwable t) {
468 
469         if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) {
470             log(SimpleLog.LOG_LEVEL_WARN, message, t);
471         }
472     }
473 
474     /**
475      * <p>
476      * Log a message with error log level.
477      * </p>
478      */
479     public final void error(Object message) {
480 
481         if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) {
482             log(SimpleLog.LOG_LEVEL_ERROR, message, null);
483         }
484     }
485 
486     /**
487      * <p>
488      * Log an error with error log level.
489      * </p>
490      */
491     public final void error(Object message, Throwable t) {
492 
493         if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) {
494             log(SimpleLog.LOG_LEVEL_ERROR, message, t);
495         }
496     }
497 
498     /**
499      * <p>
500      * Log a message with fatal log level.
501      * </p>
502      */
503     public final void fatal(Object message) {
504 
505         if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) {
506             log(SimpleLog.LOG_LEVEL_FATAL, message, null);
507         }
508     }
509 
510     /**
511      * <p>
512      * Log an error with fatal log level.
513      * </p>
514      */
515     public final void fatal(Object message, Throwable t) {
516 
517         if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) {
518             log(SimpleLog.LOG_LEVEL_FATAL, message, t);
519         }
520     }
521 
522     /**
523      * <p>
524      * Are debug messages currently enabled?
525      * </p>
526      * 
527      * <p>
528      * This allows expensive operations such as <code>String</code> concatenation
529      * to be avoided when the message will be ignored by the logger.
530      * </p>
531      */
532     public final boolean isDebugEnabled() {
533 
534         return isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG);
535     }
536 
537     /**
538      * <p>
539      * Are error messages currently enabled?
540      * </p>
541      * 
542      * <p>
543      * This allows expensive operations such as <code>String</code> concatenation
544      * to be avoided when the message will be ignored by the logger.
545      * </p>
546      */
547     public final boolean isErrorEnabled() {
548 
549         return isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR);
550     }
551 
552     /**
553      * <p>
554      * Are fatal messages currently enabled?
555      * </p>
556      * 
557      * <p>
558      * This allows expensive operations such as <code>String</code> concatenation
559      * to be avoided when the message will be ignored by the logger.
560      * </p>
561      */
562     public final boolean isFatalEnabled() {
563 
564         return isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL);
565     }
566 
567     /**
568      * <p>
569      * Are info messages currently enabled?
570      * </p>
571      * 
572      * <p>
573      * This allows expensive operations such as <code>String</code> concatenation
574      * to be avoided when the message will be ignored by the logger.
575      * </p>
576      */
577     public final boolean isInfoEnabled() {
578 
579         return isLevelEnabled(SimpleLog.LOG_LEVEL_INFO);
580     }
581 
582     /**
583      * <p>
584      * Are trace messages currently enabled?
585      * </p>
586      * 
587      * <p>
588      * This allows expensive operations such as <code>String</code> concatenation
589      * to be avoided when the message will be ignored by the logger.
590      * </p>
591      */
592     public final boolean isTraceEnabled() {
593 
594         return isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE);
595     }
596 
597     /**
598      * <p>
599      * Are warn messages currently enabled?
600      * </p>
601      * 
602      * <p>
603      * This allows expensive operations such as <code>String</code> concatenation
604      * to be avoided when the message will be ignored by the logger.
605      * </p>
606      */
607     public final boolean isWarnEnabled() {
608 
609         return isLevelEnabled(SimpleLog.LOG_LEVEL_WARN);
610     }
611 
612     /**
613      * Return the thread context class loader if available. Otherwise return null.
614      * 
615      * The thread context class loader is available for JDK 1.2 or later, if
616      * certain security conditions are met.
617      * 
618      * @exception LogConfigurationException
619      *              if a suitable class loader cannot be identified.
620      */
621     private static ClassLoader getContextClassLoader() {
622         ClassLoader classLoader = null;
623 
624         if (classLoader == null) {
625             try {
626                 // Are we running on a JDK 1.2 or later system?
627                 Method method = Thread.class.getMethod("getContextClassLoader");
628 
629                 // Get the thread context class loader (if there is one)
630                 try {
631                     classLoader = (ClassLoader) method.invoke(Thread.currentThread());
632                 } catch (IllegalAccessException e) {
633                     ; // ignore
634                 } catch (InvocationTargetException e) {
635                     /**
636                      * InvocationTargetException is thrown by 'invoke' when the method
637                      * being invoked (getContextClassLoader) throws an exception.
638                      * 
639                      * getContextClassLoader() throws SecurityException when the context
640                      * class loader isn't an ancestor of the calling class's class loader,
641                      * or if security permissions are restricted.
642                      * 
643                      * In the first case (not related), we want to ignore and keep going.
644                      * We cannot help but also ignore the second with the logic below, but
645                      * other calls elsewhere (to obtain a class loader) will trigger this
646                      * exception where we can make a distinction.
647                      */
648                     if (e.getTargetException() instanceof SecurityException) {
649                         ; // ignore
650                     } else {
651                         // Capture 'e.getTargetException()' exception for details
652                         // alternate: log 'e.getTargetException()', and pass back 'e'.
653                         throw new LogConfigurationException("Unexpected InvocationTargetException", e.getTargetException());
654                     }
655                 }
656             } catch (NoSuchMethodException e) {
657                 // Assume we are running on JDK 1.1
658                 ; // ignore
659             }
660         }
661 
662         if (classLoader == null) {
663             classLoader = SimpleLog.class.getClassLoader();
664         }
665 
666         // Return the selected class loader
667         return classLoader;
668     }
669 
670     private static InputStream getResourceAsStream(final String name) {
671         return AccessController.doPrivileged(new PrivilegedAction<InputStream>() {
672             public InputStream run() {
673                 ClassLoader threadCL = getContextClassLoader();
674 
675                 if (threadCL != null) {
676                     return threadCL.getResourceAsStream(name);
677                 } else {
678                     return ClassLoader.getSystemResourceAsStream(name);
679                 }
680             }
681         });
682     }
683 }