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",
168         showShortName);
169     showDateTime = getBooleanProperty(systemPrefix + "showdatetime",
170         showDateTime);
171 
172     if (showDateTime) {
173       dateTimeFormat = getStringProperty(systemPrefix + "dateTimeFormat",
174           dateTimeFormat);
175       try {
176         dateFormatter = new SimpleDateFormat(dateTimeFormat);
177       } catch (IllegalArgumentException e) {
178         // If the format pattern is invalid - use the default format
179         dateTimeFormat = DEFAULT_DATE_TIME_FORMAT;
180         dateFormatter = new SimpleDateFormat(dateTimeFormat);
181       }
182     }
183   }
184 
185   // ------------------------------------------------------------- Attributes
186 
187   /** The name of this simple log instance */
188   protected String logName = null;
189   /** The current log level */
190   protected int currentLogLevel;
191   /** The short name of this simple log instance */
192   private String shortLogName = null;
193 
194   // ------------------------------------------------------------ Constructor
195 
196   /**
197    * Construct a simple log with given name.
198    * 
199    * @param name
200    *          log name
201    */
202   public SimpleLog(String name) {
203 
204     logName = name;
205 
206     // Set initial log level
207     // Used to be: set default log level to ERROR
208     // IMHO it should be lower, but at least info ( costin ).
209     setLevel(SimpleLog.LOG_LEVEL_INFO);
210 
211     // Set log level from properties
212     String lvl = getStringProperty(systemPrefix + "log." + logName);
213     int i = String.valueOf(name).lastIndexOf(".");
214     while (null == lvl && i > -1) {
215       name = name.substring(0, i);
216       lvl = getStringProperty(systemPrefix + "log." + name);
217       i = String.valueOf(name).lastIndexOf(".");
218     }
219 
220     if (null == lvl) {
221       lvl = getStringProperty(systemPrefix + "defaultlog");
222     }
223 
224     if ("all".equalsIgnoreCase(lvl)) {
225       setLevel(SimpleLog.LOG_LEVEL_ALL);
226     } else if ("trace".equalsIgnoreCase(lvl)) {
227       setLevel(SimpleLog.LOG_LEVEL_TRACE);
228     } else if ("debug".equalsIgnoreCase(lvl)) {
229       setLevel(SimpleLog.LOG_LEVEL_DEBUG);
230     } else if ("info".equalsIgnoreCase(lvl)) {
231       setLevel(SimpleLog.LOG_LEVEL_INFO);
232     } else if ("warn".equalsIgnoreCase(lvl)) {
233       setLevel(SimpleLog.LOG_LEVEL_WARN);
234     } else if ("error".equalsIgnoreCase(lvl)) {
235       setLevel(SimpleLog.LOG_LEVEL_ERROR);
236     } else if ("fatal".equalsIgnoreCase(lvl)) {
237       setLevel(SimpleLog.LOG_LEVEL_FATAL);
238     } else if ("off".equalsIgnoreCase(lvl)) {
239       setLevel(SimpleLog.LOG_LEVEL_OFF);
240     }
241 
242   }
243 
244   // -------------------------------------------------------- Properties
245 
246   /**
247    * <p>
248    * Set logging level.
249    * </p>
250    * 
251    * @param currentLogLevel
252    *          new logging level
253    */
254   public void setLevel(int currentLogLevel) {
255 
256     this.currentLogLevel = currentLogLevel;
257 
258   }
259 
260   /**
261    * <p>
262    * Get logging level.
263    * </p>
264    */
265   public int getLevel() {
266 
267     return currentLogLevel;
268   }
269 
270   // -------------------------------------------------------- Logging Methods
271 
272   /**
273    * <p>
274    * Do the actual logging. This method assembles the message and then calls
275    * <code>write()</code> to cause it to be written.
276    * </p>
277    * 
278    * @param type
279    *          One of the LOG_LEVEL_XXX constants defining the log level
280    * @param message
281    *          The message itself (typically a String)
282    * @param t
283    *          The exception whose stack trace should be logged
284    */
285   protected void log(int type, Object message, Throwable t) {
286     // Use a string buffer for better performance
287     StringBuffer buf = new StringBuffer();
288 
289     // Append date-time if so configured
290     if (showDateTime) {
291       buf.append(dateFormatter.format(new Date()));
292       buf.append(" ");
293     }
294 
295     // Append a readable representation of the log level
296     switch (type) {
297     case SimpleLog.LOG_LEVEL_TRACE:
298       buf.append("[TRACE] ");
299       break;
300     case SimpleLog.LOG_LEVEL_DEBUG:
301       buf.append("[DEBUG] ");
302       break;
303     case SimpleLog.LOG_LEVEL_INFO:
304       buf.append("[INFO] ");
305       break;
306     case SimpleLog.LOG_LEVEL_WARN:
307       buf.append("[WARN] ");
308       break;
309     case SimpleLog.LOG_LEVEL_ERROR:
310       buf.append("[ERROR] ");
311       break;
312     case SimpleLog.LOG_LEVEL_FATAL:
313       buf.append("[FATAL] ");
314       break;
315     }
316 
317     // Append the name of the log instance if so configured
318     if (showShortName) {
319       if (shortLogName == null) {
320         // Cut all but the last component of the name for both styles
321         shortLogName = logName.substring(logName.lastIndexOf(".") + 1);
322         shortLogName = shortLogName
323             .substring(shortLogName.lastIndexOf("/") + 1);
324       }
325       buf.append(String.valueOf(shortLogName)).append(" - ");
326     } else if (showLogName) {
327       buf.append(String.valueOf(logName)).append(" - ");
328     }
329 
330     // Append the message
331     buf.append(String.valueOf(message));
332 
333     // Append stack trace if not null
334     if (t != null) {
335       buf.append(" <");
336       buf.append(t.toString());
337       buf.append(">");
338 
339       java.io.StringWriter sw = new java.io.StringWriter(1024);
340       java.io.PrintWriter pw = new java.io.PrintWriter(sw);
341       t.printStackTrace(pw);
342       pw.close();
343       buf.append(sw.toString());
344     }
345 
346     // Print to the appropriate destination
347     write(buf);
348 
349   }
350 
351   /**
352    * <p>
353    * Write the content of the message accumulated in the specified
354    * <code>StringBuffer</code> to the appropriate output destination. The
355    * default implementation writes to <code>System.err</code>.
356    * </p>
357    * 
358    * @param buffer
359    *          A <code>StringBuffer</code> containing the accumulated text to be
360    *          logged
361    */
362   protected void write(StringBuffer buffer) {
363 
364     System.err.println(buffer.toString());
365 
366   }
367 
368   /**
369    * Is the given log level currently enabled?
370    * 
371    * @param logLevel
372    *          is this level enabled?
373    */
374   protected boolean isLevelEnabled(int logLevel) {
375     // log level are numerically ordered so can use simple numeric
376     // comparison
377     return (logLevel >= currentLogLevel);
378   }
379 
380   // -------------------------------------------------------- Log Implementation
381 
382   /**
383    * <p>
384    * Log a message with debug log level.
385    * </p>
386    */
387   public final void debug(Object message) {
388 
389     if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) {
390       log(SimpleLog.LOG_LEVEL_DEBUG, message, null);
391     }
392   }
393 
394   /**
395    * <p>
396    * Log an error with debug log level.
397    * </p>
398    */
399   public final void debug(Object message, Throwable t) {
400 
401     if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) {
402       log(SimpleLog.LOG_LEVEL_DEBUG, message, t);
403     }
404   }
405 
406   /**
407    * <p>
408    * Log a message with trace log level.
409    * </p>
410    */
411   public final void trace(Object message) {
412 
413     if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) {
414       log(SimpleLog.LOG_LEVEL_TRACE, message, null);
415     }
416   }
417 
418   /**
419    * <p>
420    * Log an error with trace log level.
421    * </p>
422    */
423   public final void trace(Object message, Throwable t) {
424 
425     if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) {
426       log(SimpleLog.LOG_LEVEL_TRACE, message, t);
427     }
428   }
429 
430   /**
431    * <p>
432    * Log a message with info log level.
433    * </p>
434    */
435   public final void info(Object message) {
436 
437     if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) {
438       log(SimpleLog.LOG_LEVEL_INFO, message, null);
439     }
440   }
441 
442   /**
443    * <p>
444    * Log an error with info log level.
445    * </p>
446    */
447   public final void info(Object message, Throwable t) {
448 
449     if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) {
450       log(SimpleLog.LOG_LEVEL_INFO, message, t);
451     }
452   }
453 
454   /**
455    * <p>
456    * Log a message with warn log level.
457    * </p>
458    */
459   public final void warn(Object message) {
460 
461     if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) {
462       log(SimpleLog.LOG_LEVEL_WARN, message, null);
463     }
464   }
465 
466   /**
467    * <p>
468    * Log an error with warn log level.
469    * </p>
470    */
471   public final void warn(Object message, Throwable t) {
472 
473     if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) {
474       log(SimpleLog.LOG_LEVEL_WARN, message, t);
475     }
476   }
477 
478   /**
479    * <p>
480    * Log a message with error log level.
481    * </p>
482    */
483   public final void error(Object message) {
484 
485     if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) {
486       log(SimpleLog.LOG_LEVEL_ERROR, message, null);
487     }
488   }
489 
490   /**
491    * <p>
492    * Log an error with error log level.
493    * </p>
494    */
495   public final void error(Object message, Throwable t) {
496 
497     if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) {
498       log(SimpleLog.LOG_LEVEL_ERROR, message, t);
499     }
500   }
501 
502   /**
503    * <p>
504    * Log a message with fatal log level.
505    * </p>
506    */
507   public final void fatal(Object message) {
508 
509     if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) {
510       log(SimpleLog.LOG_LEVEL_FATAL, message, null);
511     }
512   }
513 
514   /**
515    * <p>
516    * Log an error with fatal log level.
517    * </p>
518    */
519   public final void fatal(Object message, Throwable t) {
520 
521     if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) {
522       log(SimpleLog.LOG_LEVEL_FATAL, message, t);
523     }
524   }
525 
526   /**
527    * <p>
528    * Are debug messages currently enabled?
529    * </p>
530    * 
531    * <p>
532    * This allows expensive operations such as <code>String</code> concatenation
533    * to be avoided when the message will be ignored by the logger.
534    * </p>
535    */
536   public final boolean isDebugEnabled() {
537 
538     return isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG);
539   }
540 
541   /**
542    * <p>
543    * Are error messages currently enabled?
544    * </p>
545    * 
546    * <p>
547    * This allows expensive operations such as <code>String</code> concatenation
548    * to be avoided when the message will be ignored by the logger.
549    * </p>
550    */
551   public final boolean isErrorEnabled() {
552 
553     return isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR);
554   }
555 
556   /**
557    * <p>
558    * Are fatal messages currently enabled?
559    * </p>
560    * 
561    * <p>
562    * This allows expensive operations such as <code>String</code> concatenation
563    * to be avoided when the message will be ignored by the logger.
564    * </p>
565    */
566   public final boolean isFatalEnabled() {
567 
568     return isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL);
569   }
570 
571   /**
572    * <p>
573    * Are info messages currently enabled?
574    * </p>
575    * 
576    * <p>
577    * This allows expensive operations such as <code>String</code> concatenation
578    * to be avoided when the message will be ignored by the logger.
579    * </p>
580    */
581   public final boolean isInfoEnabled() {
582 
583     return isLevelEnabled(SimpleLog.LOG_LEVEL_INFO);
584   }
585 
586   /**
587    * <p>
588    * Are trace messages currently enabled?
589    * </p>
590    * 
591    * <p>
592    * This allows expensive operations such as <code>String</code> concatenation
593    * to be avoided when the message will be ignored by the logger.
594    * </p>
595    */
596   public final boolean isTraceEnabled() {
597 
598     return isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE);
599   }
600 
601   /**
602    * <p>
603    * Are warn messages currently enabled?
604    * </p>
605    * 
606    * <p>
607    * This allows expensive operations such as <code>String</code> concatenation
608    * to be avoided when the message will be ignored by the logger.
609    * </p>
610    */
611   public final boolean isWarnEnabled() {
612 
613     return isLevelEnabled(SimpleLog.LOG_LEVEL_WARN);
614   }
615 
616   /**
617    * Return the thread context class loader if available. Otherwise return null.
618    * 
619    * The thread context class loader is available for JDK 1.2 or later, if
620    * certain security conditions are met.
621    * 
622    * @exception LogConfigurationException
623    *              if a suitable class loader cannot be identified.
624    */
625   private static ClassLoader getContextClassLoader() {
626     ClassLoader classLoader = null;
627 
628     if (classLoader == null) {
629       try {
630         // Are we running on a JDK 1.2 or later system?
631         Method method = Thread.class.getMethod("getContextClassLoader", null);
632 
633         // Get the thread context class loader (if there is one)
634         try {
635           classLoader = (ClassLoader) method.invoke(Thread.currentThread(),
636               null);
637         } catch (IllegalAccessException e) {
638           ; // ignore
639         } catch (InvocationTargetException e) {
640           /**
641            * InvocationTargetException is thrown by 'invoke' when the method
642            * being invoked (getContextClassLoader) throws an exception.
643            * 
644            * getContextClassLoader() throws SecurityException when the context
645            * class loader isn't an ancestor of the calling class's class loader,
646            * or if security permissions are restricted.
647            * 
648            * In the first case (not related), we want to ignore and keep going.
649            * We cannot help but also ignore the second with the logic below, but
650            * other calls elsewhere (to obtain a class loader) will trigger this
651            * exception where we can make a distinction.
652            */
653           if (e.getTargetException() instanceof SecurityException) {
654             ; // ignore
655           } else {
656             // Capture 'e.getTargetException()' exception for details
657             // alternate: log 'e.getTargetException()', and pass back 'e'.
658             throw new LogConfigurationException(
659                 "Unexpected InvocationTargetException", e.getTargetException());
660           }
661         }
662       } catch (NoSuchMethodException e) {
663         // Assume we are running on JDK 1.1
664         ; // ignore
665       }
666     }
667 
668     if (classLoader == null) {
669       classLoader = SimpleLog.class.getClassLoader();
670     }
671 
672     // Return the selected class loader
673     return classLoader;
674   }
675 
676   private static InputStream getResourceAsStream(final String name) {
677     return (InputStream) AccessController.doPrivileged(new PrivilegedAction() {
678       public Object run() {
679         ClassLoader threadCL = getContextClassLoader();
680 
681         if (threadCL != null) {
682           return threadCL.getResourceAsStream(name);
683         } else {
684           return ClassLoader.getSystemResourceAsStream(name);
685         }
686       }
687     });
688   }
689 }