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