View Javadoc

1   /**
2    * Copyright (c) 2004-2011 QOS.ch
3    * All rights reserved.
4    *
5    * Permission is hereby granted, free of charge, to any person obtaining
6    * a copy of this software and associated documentation files (the
7    * "Software"), to  deal in  the Software without  restriction, including
8    * without limitation  the rights to  use, copy, modify,  merge, publish,
9    * distribute,  sublicense, and/or sell  copies of  the Software,  and to
10   * permit persons to whom the Software  is furnished to do so, subject to
11   * the following conditions:
12   *
13   * The  above  copyright  notice  and  this permission  notice  shall  be
14   * included in all copies or substantial portions of the Software.
15   *
16   * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
17   * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
18   * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
19   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20   * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21   * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
22   * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23   *
24   */
25  
26  package org.slf4j.impl;
27  
28  import java.io.InputStream;
29  import java.security.AccessController;
30  import java.security.PrivilegedAction;
31  import java.text.DateFormat;
32  import java.text.SimpleDateFormat;
33  import java.util.Date;
34  import java.util.Properties;
35  
36  import org.slf4j.Logger;
37  import org.slf4j.helpers.FormattingTuple;
38  import org.slf4j.helpers.MarkerIgnoringBase;
39  import org.slf4j.helpers.MessageFormatter;
40  import org.slf4j.helpers.Util;
41  import org.slf4j.spi.LocationAwareLogger;
42  
43  /**
44   * <p>Simple implementation of {@link Logger} that sends all enabled log messages,
45   * for all defined loggers, to the console ({@code System.err}).
46   * The following system properties are supported to configure the behavior of this logger:</p>
47   * <ul>
48   * <li><code>org.slf4j.simplelogger.defaultlog</code> -
49   *     Default logging detail level for all instances of SimpleLogger.
50   *     Must be one of ("trace", "debug", "info", "warn", or "error").
51   *     If not specified, defaults to "info". </li>
52   * <li><code>org.slf4j.simplelogger.log.xxxxx</code> -
53   *     Logging detail level for a SimpleLogger instance named "xxxxx".
54   *     Must be one of ("trace", "debug", "info", "warn", or "error").
55   *     If not specified, the default logging detail level is used.</li>
56   * <li><code>org.slf4j.simplelogger.showdatetime</code> -
57   *     Set to <code>true</code> if you want the current date and time
58   *     to be included in output messages. Default is <code>false</code>,
59   *     and will output the number of milliseconds elapsed since startup.</li>
60   * <li><code>org.slf4j.simplelogger.dateTimeFormat</code> -
61   *     The date and time format to be used in the output messages.
62   *     The pattern describing the date and time format is the same that is
63   *     used in <code>java.text.SimpleDateFormat</code>. If the format is not
64   *     specified or is invalid, the default format is used.
65   *     The default format is <code>yyyy-MM-dd HH:mm:ss:SSS Z</code>.</li>
66   * <li><code>org.slf4j.simplelogger.showthreadname</code> -
67   *     Set to <code>true</code> if you want to output the current thread name.
68   *     Defaults to <code>true</code>.</li>
69   * <li><code>org.slf4j.simplelogger.showlogname</code> -
70   *     Set to <code>true</code> if you want the Logger instance name to be
71   *     included in output messages. Defaults to <code>true</code>.</li>
72   * <li><code>org.slf4j.simplelogger.showShortLogname</code> -
73   *     Set to <code>true</code> if you want the last component of the name to be
74   *     included in output messages. Defaults to <code>false</code>.</li>
75   * </ul>
76   *
77   * <p>In addition to looking for system properties with the names specified
78   * above, this implementation also checks for a class loader resource named
79   * <code>"simplelogger.properties"</code>, and includes any matching definitions
80   * from this resource (if it exists).</p>
81   *
82   *
83   * <p>With no configurationn, the default output includes the relative time in milliseconds,
84   * thread name, the level, logger name, and the message followed by the line
85   * separator for the host.  In log4j terms it amounts to the "%r [%t]
86   * %level %logger - %m%n" pattern. </p>
87   *
88   * <p>Sample output follows.</p>
89  <pre>
90  176 [main] INFO examples.Sort - Populating an array of 2 elements in reverse order.
91  225 [main] INFO examples.SortAlgo - Entered the sort method.
92  304 [main] INFO examples.SortAlgo - Dump of integer array:
93  317 [main] INFO examples.SortAlgo - Element [0] = 0
94  331 [main] INFO examples.SortAlgo - Element [1] = 1
95  343 [main] INFO examples.Sort - The next log statement should be an error message.
96  346 [main] ERROR examples.SortAlgo - Tried to dump an uninitialized array.
97          at org.log4j.examples.SortAlgo.dump(SortAlgo.java:58)
98          at org.log4j.examples.Sort.main(Sort.java:64)
99  467 [main] INFO  examples.Sort - Exiting main method.
100 </pre>
101  *
102  * <p>This implementation is heavily inspired by
103  * <a href="http://commons.apache.org/logging/">Apache Commons Logging</a>'s SimpleLog.
104  *
105  * @author Ceki G&uuml;lc&uuml;
106  * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
107  * @author Rod Waldhoff
108  * @author Robert Burrell Donkin
109  * @author C&eacute;drik LIME
110  */
111 public class SimpleLogger extends MarkerIgnoringBase {
112 
113   private static final long serialVersionUID = -632788891211436180L;
114 
115   /**
116    * Mark the time when this class gets loaded into memory.
117    */
118   private static long startTime = System.currentTimeMillis();
119 
120   private static final String CONFIGURATION_FILE = "simplelogger.properties";
121 
122   /** All system properties used by <code>SimpleLogger</code> start with this */
123   private static final String systemPrefix = "org.slf4j.simplelogger.";
124 
125   /** Properties loaded from simplelogger.properties */
126   private static final Properties simpleLoggerProps = new Properties();
127 
128   /** The default format to use when formating dates */
129   private static final String DEFAULT_DATE_TIME_FORMAT =
130       "yyyy-MM-dd HH:mm:ss:SSS Z";
131 
132   /** Include the instance name in the log message? */
133   private static boolean showLogName = true;
134   /** Include the short name ( last component ) of the logger in the log
135    *  message. Defaults to true - otherwise we'll be lost in a flood of
136    *  messages without knowing who sends them.
137    */
138   private static boolean showShortName = false;
139   /** Include the current time in the log message */
140   private static boolean showDateTime = false;
141   /** The date and time format to use in the log message */
142   private static String dateTimeFormat = DEFAULT_DATE_TIME_FORMAT;
143 
144   /** Include the current thread name in the log message */
145   private static boolean showThreadName = true;
146 
147   /**
148    * Used to format times.
149    * <p>
150    * Any code that accesses this object should first obtain a lock on it,
151    * ie use synchronized(dateFormatter); this requirement is
152    * to fix an existing thread safety bug (SimpleDateFormat.format
153    * is not thread-safe).
154    */
155   private static DateFormat dateFormatter = null;
156 
157   /** "Trace" level logging. */
158   public static final int LOG_LEVEL_TRACE  = LocationAwareLogger.TRACE_INT;
159   /** "Debug" level logging. */
160   public static final int LOG_LEVEL_DEBUG  = LocationAwareLogger.DEBUG_INT;
161   /** "Info" level logging. */
162   public static final int LOG_LEVEL_INFO   = LocationAwareLogger.INFO_INT;
163   /** "Warn" level logging. */
164   public static final int LOG_LEVEL_WARN   = LocationAwareLogger.WARN_INT;
165   /** "Error" level logging. */
166   public static final int LOG_LEVEL_ERROR  = LocationAwareLogger.ERROR_INT;
167   /** "Fatal" level logging. */
168 //  public static final int LOG_LEVEL_FATAL  = 6;
169 
170   /** Enable all logging levels */
171   public static final int LOG_LEVEL_ALL    = (LOG_LEVEL_TRACE - 10);
172 
173   /** Enable no logging levels */
174   public static final int LOG_LEVEL_OFF    = (LOG_LEVEL_ERROR + 10);
175 
176 
177   private static String getStringProperty(String name) {
178       String prop = null;
179       try {
180           prop = System.getProperty(name);
181       } catch (SecurityException e) {
182           ; // Ignore
183       }
184       return (prop == null) ? simpleLoggerProps.getProperty(name) : prop;
185   }
186 
187   private static String getStringProperty(String name, String defaultValue) {
188       String prop = getStringProperty(name);
189       return (prop == null) ? defaultValue : prop;
190   }
191 
192   private static boolean getBooleanProperty(String name, boolean defaultValue) {
193       String prop = getStringProperty(name);
194       return (prop == null) ? defaultValue : "true".equalsIgnoreCase(prop);
195   }
196 
197   // Initialize class attributes.
198   // Load properties file, if found.
199   // Override with system properties.
200   static {
201       // Add props from the resource simplelogger.properties
202       InputStream in = (InputStream)AccessController.doPrivileged(
203               new PrivilegedAction() {
204                   public Object run() {
205                       ClassLoader threadCL = Thread.currentThread().getContextClassLoader();
206                       if (threadCL != null) {
207                           return threadCL.getResourceAsStream(CONFIGURATION_FILE);
208                       } else {
209                           return ClassLoader.getSystemResourceAsStream(CONFIGURATION_FILE);
210                       }
211                   }
212               });
213       if(null != in) {
214           try {
215               simpleLoggerProps.load(in);
216               in.close();
217           } catch(java.io.IOException e) {
218               // ignored
219           }
220       }
221 
222       showLogName    = getBooleanProperty(systemPrefix + "showlogname",      showLogName);
223       showShortName  = getBooleanProperty(systemPrefix + "showShortLogname", showShortName);
224       showDateTime   = getBooleanProperty(systemPrefix + "showdatetime",     showDateTime);
225       showThreadName = getBooleanProperty(systemPrefix + "showthreadname",   showThreadName);
226       dateTimeFormat = getStringProperty(systemPrefix + "dateTimeFormat",    dateTimeFormat);
227 
228       if(showDateTime) {
229           try {
230               dateFormatter = new SimpleDateFormat(dateTimeFormat);
231           } catch(IllegalArgumentException e) {
232               Util.report("Bad date format in " + CONFIGURATION_FILE + "; reverting to default", e);
233               // If the format pattern is invalid - use the default format
234               dateTimeFormat = DEFAULT_DATE_TIME_FORMAT;
235               dateFormatter = new SimpleDateFormat(dateTimeFormat);
236           }
237       }
238   }
239 
240 
241   /** The name of this simple log instance */
242   //protected String logName = null;// == name
243   /** The current log level */
244   protected int currentLogLevel = LOG_LEVEL_INFO;
245   /** The short name of this simple log instance */
246   private transient String shortLogName = null;
247 
248   /**
249    * Package access allows only {@link SimpleLoggerFactory} to instantiate
250    * SimpleLogger instances.
251    */
252   SimpleLogger(String name) {
253     this.name = name;
254 
255     // Set initial log level
256     this.currentLogLevel = LOG_LEVEL_INFO;
257 
258     // Set log level from properties
259     String lvl = getStringProperty(systemPrefix + "log." + name);
260     int i = String.valueOf(name).lastIndexOf(".");
261     while(null == lvl && i > -1) {
262         name = name.substring(0,i);
263         lvl = getStringProperty(systemPrefix + "log." + name);
264         i = String.valueOf(name).lastIndexOf(".");
265     }
266 
267     if(null == lvl) {
268         lvl =  getStringProperty(systemPrefix + "defaultlog");
269     }
270 
271     if("all".equalsIgnoreCase(lvl)) {
272     	this.currentLogLevel = LOG_LEVEL_ALL;
273     } else if("trace".equalsIgnoreCase(lvl)) {
274     	this.currentLogLevel = LOG_LEVEL_TRACE;
275     } else if("debug".equalsIgnoreCase(lvl)) {
276     	this.currentLogLevel = LOG_LEVEL_DEBUG;
277     } else if("info".equalsIgnoreCase(lvl)) {
278     	this.currentLogLevel = LOG_LEVEL_INFO;
279     } else if("warn".equalsIgnoreCase(lvl)) {
280     	this.currentLogLevel = LOG_LEVEL_WARN;
281     } else if("error".equalsIgnoreCase(lvl)) {
282     	this.currentLogLevel = LOG_LEVEL_ERROR;
283 //    } else if("fatal".equalsIgnoreCase(lvl)) {
284 //        setLevel(LOG_LEVEL_FATAL);
285     } else if("off".equalsIgnoreCase(lvl)) {
286     	this.currentLogLevel = LOG_LEVEL_OFF;
287     }
288   }
289 
290 
291   /**
292    * This is our internal implementation for logging regular (non-parameterized)
293    * log messages.
294    *
295    * @param level One of the LOG_LEVEL_XXX constants defining the log level
296    * @param message The message itself
297    * @param t The exception whose stack trace should be logged
298    */
299   private void log(int level, String message, Throwable t) {
300     if (! isLevelEnabled(level)) {
301       return;
302     }
303 
304     StringBuffer buf = new StringBuffer(32);
305 
306     // Append date-time if so configured
307     if(showDateTime) {
308       Date now = new Date();
309       String dateText;
310       synchronized(dateFormatter) {
311         dateText = dateFormatter.format(now);
312       }
313       buf.append(dateText);
314       buf.append(' ');
315     } else {
316       buf.append(System.currentTimeMillis() - startTime);
317       buf.append(' ');
318     }
319 
320     // Append current thread name if so configured
321     if (showThreadName) {
322       buf.append('[');
323       buf.append(Thread.currentThread().getName());
324       buf.append("] ");
325 	}
326 
327     // Append a readable representation of the log level
328     switch(level) {
329       case LOG_LEVEL_TRACE: buf.append("TRACE"); break;
330       case LOG_LEVEL_DEBUG: buf.append("DEBUG"); break;
331       case LOG_LEVEL_INFO:  buf.append("INFO");  break;
332       case LOG_LEVEL_WARN:  buf.append("WARN");  break;
333       case LOG_LEVEL_ERROR: buf.append("ERROR"); break;
334 //      case LOG_LEVEL_FATAL: buf.append("[FATAL] "); break;
335     }
336     buf.append(' ');
337 
338     // Append the name of the log instance if so configured
339     if(showShortName) {
340       if(shortLogName==null) {
341         // Cut all but the last component of the name for both styles
342         shortLogName = name.substring(name.lastIndexOf(".") + 1);
343         shortLogName =
344             shortLogName.substring(shortLogName.lastIndexOf("/") + 1);
345       }
346       buf.append(String.valueOf(shortLogName)).append(" - ");
347     } else if(showLogName) {
348         buf.append(String.valueOf(name)).append(" - ");
349     }
350 
351     // Append the message
352     buf.append(message);
353 
354     System.err.println(buf.toString());
355     // Append stack trace if not null
356     if (t != null) {
357       t.printStackTrace(System.err);
358     }
359     System.err.flush();
360   }
361 
362   /**
363    * For formatted messages, first substitute arguments and then log.
364    *
365    * @param level
366    * @param format
367    * @param param1
368    * @param param2
369    */
370   private void formatAndLog(int level, String format, Object arg1,
371       Object arg2) {
372     if (! isLevelEnabled(level)) {
373       return;
374     }
375     FormattingTuple tp = MessageFormatter.format(format, arg1, arg2);
376     log(level, tp.getMessage(), tp.getThrowable());
377   }
378 
379   /**
380    * For formatted messages, first substitute arguments and then log.
381    *
382    * @param level
383    * @param format
384    * @param argArray
385    */
386   private void formatAndLog(int level, String format, Object[] argArray) {
387     if (! isLevelEnabled(level)) {
388       return;
389     }
390     FormattingTuple tp = MessageFormatter.arrayFormat(format, argArray);
391     log(level, tp.getMessage(), tp.getThrowable());
392   }
393 
394   /**
395    * Is the given log level currently enabled?
396    *
397    * @param logLevel is this level enabled?
398    */
399   protected boolean isLevelEnabled(int logLevel) {
400 	  // log level are numerically ordered so can use simple numeric
401 	  // comparison
402 	  return (logLevel >= currentLogLevel);
403   }
404 
405 
406   /**
407    * Are {@code trace} messages currently enabled?
408    */
409   public boolean isTraceEnabled() {
410     return isLevelEnabled(LOG_LEVEL_TRACE);
411   }
412 
413   /**
414    * A simple implementation which logs messages of level TRACE according
415    * to the format outlined above.
416    */
417   public void trace(String msg) {
418     log(LOG_LEVEL_TRACE, msg, null);
419   }
420 
421   /**
422    * Perform single parameter substitution before logging the message of level
423    * TRACE according to the format outlined above.
424    */
425   public void trace(String format, Object param1) {
426     formatAndLog(LOG_LEVEL_TRACE, format, param1, null);
427   }
428 
429   /**
430    * Perform double parameter substitution before logging the message of level
431    * TRACE according to the format outlined above.
432    */
433   public void trace(String format, Object param1, Object param2) {
434     formatAndLog(LOG_LEVEL_TRACE, format, param1, param2);
435   }
436 
437   /**
438    * Perform double parameter substitution before logging the message of level
439    * TRACE according to the format outlined above.
440    */
441   public void trace(String format, Object[] argArray) {
442     formatAndLog(LOG_LEVEL_TRACE, format, argArray);
443   }
444 
445   /**
446    * Log a message of level TRACE, including an exception.
447    */
448   public void trace(String msg, Throwable t) {
449     log(LOG_LEVEL_TRACE, msg, t);
450   }
451 
452   /**
453    * Are {@code debug} messages currently enabled?
454    */
455   public boolean isDebugEnabled() {
456     return isLevelEnabled(LOG_LEVEL_DEBUG);
457   }
458 
459   /**
460    * A simple implementation which logs messages of level DEBUG according
461    * to the format outlined above.
462    */
463   public void debug(String msg) {
464     log(LOG_LEVEL_DEBUG, msg, null);
465   }
466 
467   /**
468    * Perform single parameter substitution before logging the message of level
469    * DEBUG according to the format outlined above.
470    */
471   public void debug(String format, Object param1) {
472     formatAndLog(LOG_LEVEL_DEBUG, format, param1, null);
473   }
474 
475   /**
476    * Perform double parameter substitution before logging the message of level
477    * DEBUG according to the format outlined above.
478    */
479   public void debug(String format, Object param1, Object param2) {
480     formatAndLog(LOG_LEVEL_DEBUG, format, param1, param2);
481   }
482 
483   /**
484    * Perform double parameter substitution before logging the message of level
485    * DEBUG according to the format outlined above.
486    */
487   public void debug(String format, Object[] argArray) {
488     formatAndLog(LOG_LEVEL_DEBUG, format, argArray);
489   }
490 
491   /**
492    * Log a message of level DEBUG, including an exception.
493    */
494   public void debug(String msg, Throwable t) {
495     log(LOG_LEVEL_DEBUG, msg, t);
496   }
497 
498   /**
499    * Are {@code info} messages currently enabled?
500    */
501   public boolean isInfoEnabled() {
502     return isLevelEnabled(LOG_LEVEL_INFO);
503   }
504 
505   /**
506    * A simple implementation which logs messages of level INFO according
507    * to the format outlined above.
508    */
509   public void info(String msg) {
510     log(LOG_LEVEL_INFO, msg, null);
511   }
512 
513   /**
514    * Perform single parameter substitution before logging the message of level
515    * INFO according to the format outlined above.
516    */
517   public void info(String format, Object arg) {
518     formatAndLog(LOG_LEVEL_INFO, format, arg, null);
519   }
520 
521   /**
522    * Perform double parameter substitution before logging the message of level
523    * INFO according to the format outlined above.
524    */
525   public void info(String format, Object arg1, Object arg2) {
526     formatAndLog(LOG_LEVEL_INFO, format, arg1, arg2);
527   }
528 
529   /**
530    * Perform double parameter substitution before logging the message of level
531    * INFO according to the format outlined above.
532    */
533   public void info(String format, Object[] argArray) {
534     formatAndLog(LOG_LEVEL_INFO, format, argArray);
535   }
536 
537   /**
538    * Log a message of level INFO, including an exception.
539    */
540   public void info(String msg, Throwable t) {
541     log(LOG_LEVEL_INFO, msg, t);
542   }
543 
544   /**
545    * Are {@code warn} messages currently enabled?
546    */
547   public boolean isWarnEnabled() {
548     return isLevelEnabled(LOG_LEVEL_WARN);
549   }
550 
551   /**
552    * A simple implementation which always logs messages of level WARN according
553    * to the format outlined above.
554    */
555   public void warn(String msg) {
556     log(LOG_LEVEL_WARN, msg, null);
557   }
558 
559   /**
560    * Perform single parameter substitution before logging the message of level
561    * WARN according to the format outlined above.
562    */
563   public void warn(String format, Object arg) {
564     formatAndLog(LOG_LEVEL_WARN, format, arg, null);
565   }
566 
567   /**
568    * Perform double parameter substitution before logging the message of level
569    * WARN according to the format outlined above.
570    */
571   public void warn(String format, Object arg1, Object arg2) {
572     formatAndLog(LOG_LEVEL_WARN, format, arg1, arg2);
573   }
574 
575   /**
576    * Perform double parameter substitution before logging the message of level
577    * WARN according to the format outlined above.
578    */
579   public void warn(String format, Object[] argArray) {
580     formatAndLog(LOG_LEVEL_WARN, format, argArray);
581   }
582 
583   /**
584    * Log a message of level WARN, including an exception.
585    */
586   public void warn(String msg, Throwable t) {
587     log(LOG_LEVEL_WARN, msg, t);
588   }
589 
590   /**
591    * Are {@code error} messages currently enabled?
592    */
593   public boolean isErrorEnabled() {
594     return isLevelEnabled(LOG_LEVEL_ERROR);
595   }
596 
597   /**
598    * A simple implementation which always logs messages of level ERROR according
599    * to the format outlined above.
600    */
601   public void error(String msg) {
602     log(LOG_LEVEL_ERROR, msg, null);
603   }
604 
605   /**
606    * Perform single parameter substitution before logging the message of level
607    * ERROR according to the format outlined above.
608    */
609   public void error(String format, Object arg) {
610     formatAndLog(LOG_LEVEL_ERROR, format, arg, null);
611   }
612 
613   /**
614    * Perform double parameter substitution before logging the message of level
615    * ERROR according to the format outlined above.
616    */
617   public void error(String format, Object arg1, Object arg2) {
618     formatAndLog(LOG_LEVEL_ERROR, format, arg1, arg2);
619   }
620 
621   /**
622    * Perform double parameter substitution before logging the message of level
623    * ERROR according to the format outlined above.
624    */
625   public void error(String format, Object[] argArray) {
626     formatAndLog(LOG_LEVEL_ERROR, format, argArray);
627   }
628 
629   /**
630    * Log a message of level ERROR, including an exception.
631    */
632   public void error(String msg, Throwable t) {
633     log(LOG_LEVEL_ERROR, msg, t);
634   }
635 }