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