View Javadoc

    1   /**
    2    * Copyright (c) 2004-2012 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  package org.slf4j.impl;
    26
    27  import java.io.PrintStream;
    28  import java.util.Date;
    29
    30  import org.slf4j.Logger;
    31  import org.slf4j.event.LoggingEvent;
    32  import org.slf4j.helpers.FormattingTuple;
    33  import org.slf4j.helpers.MarkerIgnoringBase;
    34  import org.slf4j.helpers.MessageFormatter;
    35  import org.slf4j.spi.LocationAwareLogger;
    36
    37  /**
    38   * <p>
    39   * Simple implementation of {@link Logger} that sends all enabled log messages,
    40   * for all defined loggers, to the console ({@code System.err}). The following
    41   * system properties are supported to configure the behavior of this logger:
    42   * </p>
    43   *
    44   * <ul>
    45   * <li><code>org.slf4j.simpleLogger.logFile</code> - The output target which can
    46   * be the <em>path</em> to a file, or the special values "System.out" and
    47   * "System.err". Default is "System.err".</li>
    48   *
    49   * <li><code>org.slf4j.simpleLogger.cacheOutputStream</code> - If the output
    50   * target is set to "System.out" or "System.err" (see preceding entry), by
    51   * default, logs will be output to the latest value referenced by
    52   * <code>System.out/err</code> variables. By setting this
    53   * parameter to true, the output stream will be cached, i.e. assigned once at
    54   * initialization time and re-used independently of the current value referenced by
    55   *  <code>System.out/err</code>.
    56   * </li>
    57   *
    58   * <li><code>org.slf4j.simpleLogger.defaultLogLevel</code> - Default log level
    59   * for all instances of SimpleLogger. Must be one of ("trace", "debug", "info",
    60   * "warn", "error" or "off"). If not specified, defaults to "info".</li>
    61   *
    62   * <li><code>org.slf4j.simpleLogger.log.<em>a.b.c</em></code> - Logging detail
    63   * level for a SimpleLogger instance named "a.b.c". Right-side value must be one
    64   * of "trace", "debug", "info", "warn", "error" or "off". When a SimpleLogger
    65   * named "a.b.c" is initialized, its level is assigned from this property. If
    66   * unspecified, the level of nearest parent logger will be used, and if none is
    67   * set, then the value specified by
    68   * <code>org.slf4j.simpleLogger.defaultLogLevel</code> will be used.</li>
    69   *
    70   * <li><code>org.slf4j.simpleLogger.showDateTime</code> - Set to
    71   * <code>true</code> if you want the current date and time to be included in
    72   * output messages. Default is <code>false</code></li>
    73   *
    74   * <li><code>org.slf4j.simpleLogger.dateTimeFormat</code> - The date and time
    75   * format to be used in the output messages. The pattern describing the date and
    76   * time format is defined by <a href=
    77   * "http://docs.oracle.com/javase/1.5.0/docs/api/java/text/SimpleDateFormat.html">
    78   * <code>SimpleDateFormat</code></a>. If the format is not specified or is
    79   * invalid, the number of milliseconds since start up will be output.</li>
    80   *
    81   * <li><code>org.slf4j.simpleLogger.showThreadName</code> -Set to
    82   * <code>true</code> if you want to output the current thread name. Defaults to
    83   * <code>true</code>.</li>
    84   *
    85   * <li><code>org.slf4j.simpleLogger.showLogName</code> - Set to
    86   * <code>true</code> if you want the Logger instance name to be included in
    87   * output messages. Defaults to <code>true</code>.</li>
    88   *
    89   * <li><code>org.slf4j.simpleLogger.showShortLogName</code> - Set to
    90   * <code>true</code> if you want the last component of the name to be included
    91   * in output messages. Defaults to <code>false</code>.</li>
    92   *
    93   * <li><code>org.slf4j.simpleLogger.levelInBrackets</code> - Should the level
    94   * string be output in brackets? Defaults to <code>false</code>.</li>
    95   *
    96   * <li><code>org.slf4j.simpleLogger.warnLevelString</code> - The string value
    97   * output for the warn level. Defaults to <code>WARN</code>.</li>
    98   *
    99   * </ul>
    100  *
    101  * <p>
    102  * In addition to looking for system properties with the names specified above,
    103  * this implementation also checks for a class loader resource named
    104  * <code>"simplelogger.properties"</code>, and includes any matching definitions
    105  * from this resource (if it exists).
    106  * </p>
    107  *
    108  * <p>
    109  * With no configuration, the default output includes the relative time in
    110  * milliseconds, thread name, the level, logger name, and the message followed
    111  * by the line separator for the host. In log4j terms it amounts to the "%r [%t]
    112  * %level %logger - %m%n" pattern.
    113  * </p>
    114  * <p>
    115  * Sample output follows.
    116  * </p>
    117  *
    118  * <pre>
    119  * 176 [main] INFO examples.Sort - Populating an array of 2 elements in reverse order.
    120  * 225 [main] INFO examples.SortAlgo - Entered the sort method.
    121  * 304 [main] INFO examples.SortAlgo - Dump of integer array:
    122  * 317 [main] INFO examples.SortAlgo - Element [0] = 0
    123  * 331 [main] INFO examples.SortAlgo - Element [1] = 1
    124  * 343 [main] INFO examples.Sort - The next log statement should be an error message.
    125  * 346 [main] ERROR examples.SortAlgo - Tried to dump an uninitialized array.
    126  *   at org.log4j.examples.SortAlgo.dump(SortAlgo.java:58)
    127  *   at org.log4j.examples.Sort.main(Sort.java:64)
    128  * 467 [main] INFO  examples.Sort - Exiting main method.
    129  * </pre>
    130  *
    131  * <p>
    132  * This implementation is heavily inspired by
    133  * <a href="http://commons.apache.org/logging/">Apache Commons Logging</a>'s
    134  * SimpleLog.
    135  * </p>
    136  *
    137  * @author Ceki G&uuml;lc&uuml;
    138  * @author Scott Sanders
    139  * @author Rod Waldhoff
    140  * @author Robert Burrell Donkin
    141  * @author C&eacute;drik LIME
    142  */
    143 public class SimpleLogger extends MarkerIgnoringBase {
    144
    145     private static final long serialVersionUID = -632788891211436180L;
    146
    147     private static long START_TIME = System.currentTimeMillis();
    148
    149     protected static final int LOG_LEVEL_TRACE = LocationAwareLogger.TRACE_INT;
    150     protected static final int LOG_LEVEL_DEBUG = LocationAwareLogger.DEBUG_INT;
    151     protected static final int LOG_LEVEL_INFO = LocationAwareLogger.INFO_INT;
    152     protected static final int LOG_LEVEL_WARN = LocationAwareLogger.WARN_INT;
    153     protected static final int LOG_LEVEL_ERROR = LocationAwareLogger.ERROR_INT;
    154     // The OFF level can only be used in configuration files to disable logging.
    155     // It has
    156     // no printing method associated with it in o.s.Logger interface.
    157     protected static final int LOG_LEVEL_OFF = LOG_LEVEL_ERROR + 10;
    158
    159     private static boolean INITIALIZED = false;
    160     static SimpleLoggerConfiguration CONFIG_PARAMS = null;
    161
    162     static void lazyInit() {
    163         if (INITIALIZED) {
    164             return;
    165         }
    166         INITIALIZED = true;
    167         init();
    168     }
    169
    170     // external software might be invoking this method directly. Do not rename
    171     // or change its semantics.
    172     static void init() {
    173         CONFIG_PARAMS = new SimpleLoggerConfiguration();
    174         CONFIG_PARAMS.init();
    175     }
    176
    177     /** The current log level */
    178     protected int currentLogLevel = LOG_LEVEL_INFO;
    179     /** The short name of this simple log instance */
    180     private transient String shortLogName = null;
    181
    182     /**
    183      * All system properties used by <code>SimpleLogger</code> start with this
    184      * prefix
    185      */
    186     public static final String SYSTEM_PREFIX = "org.slf4j.simpleLogger.";
    187
    188     public static final String LOG_KEY_PREFIX = SimpleLogger.SYSTEM_PREFIX + "log.";
    189
    190     public static final String CACHE_OUTPUT_STREAM_STRING_KEY = SimpleLogger.SYSTEM_PREFIX + "cacheOutputStream";
    191
    192     public static final String WARN_LEVEL_STRING_KEY = SimpleLogger.SYSTEM_PREFIX + "warnLevelString";
    193
    194     public static final String LEVEL_IN_BRACKETS_KEY = SimpleLogger.SYSTEM_PREFIX + "levelInBrackets";
    195
    196     public static final String LOG_FILE_KEY = SimpleLogger.SYSTEM_PREFIX + "logFile";
    197
    198     public static final String SHOW_SHORT_LOG_NAME_KEY = SimpleLogger.SYSTEM_PREFIX + "showShortLogName";
    199
    200     public static final String SHOW_LOG_NAME_KEY = SimpleLogger.SYSTEM_PREFIX + "showLogName";
    201
    202     public static final String SHOW_THREAD_NAME_KEY = SimpleLogger.SYSTEM_PREFIX + "showThreadName";
    203
    204     public static final String DATE_TIME_FORMAT_KEY = SimpleLogger.SYSTEM_PREFIX + "dateTimeFormat";
    205
    206     public static final String SHOW_DATE_TIME_KEY = SimpleLogger.SYSTEM_PREFIX + "showDateTime";
    207
    208     public static final String DEFAULT_LOG_LEVEL_KEY = SimpleLogger.SYSTEM_PREFIX + "defaultLogLevel";
    209
    210     /**
    211      * Package access allows only {@link SimpleLoggerFactory} to instantiate
    212      * SimpleLogger instances.
    213      */
    214     SimpleLogger(String name) {
    215         this.name = name;
    216
    217         String levelString = recursivelyComputeLevelString();
    218         if (levelString != null) {
    219             this.currentLogLevel = SimpleLoggerConfiguration.stringToLevel(levelString);
    220         } else {
    221             this.currentLogLevel = CONFIG_PARAMS.defaultLogLevel;
    222         }
    223     }
    224
    225     String recursivelyComputeLevelString() {
    226         String tempName = name;
    227         String levelString = null;
    228         int indexOfLastDot = tempName.length();
    229         while ((levelString == null) && (indexOfLastDot > -1)) {
    230             tempName = tempName.substring(0, indexOfLastDot);
    231             levelString = CONFIG_PARAMS.getStringProperty(SimpleLogger.LOG_KEY_PREFIX + tempName, null);
    232             indexOfLastDot = String.valueOf(tempName).lastIndexOf(".");
    233         }
    234         return levelString;
    235     }
    236
    237     /**
    238      * This is our internal implementation for logging regular
    239      * (non-parameterized) log messages.
    240      *
    241      * @param level
    242      *            One of the LOG_LEVEL_XXX constants defining the log level
    243      * @param message
    244      *            The message itself
    245      * @param t
    246      *            The exception whose stack trace should be logged
    247      */
    248     private void log(int level, String message, Throwable t) {
    249         if (!isLevelEnabled(level)) {
    250             return;
    251         }
    252
    253         StringBuilder buf = new StringBuilder(32);
    254
    255         // Append date-time if so configured
    256         if (CONFIG_PARAMS.showDateTime) {
    257             if (CONFIG_PARAMS.dateFormatter != null) {
    258                 buf.append(getFormattedDate());
    259                 buf.append(' ');
    260             } else {
    261                 buf.append(System.currentTimeMillis() - START_TIME);
    262                 buf.append(' ');
    263             }
    264         }
    265
    266         // Append current thread name if so configured
    267         if (CONFIG_PARAMS.showThreadName) {
    268             buf.append('[');
    269             buf.append(Thread.currentThread().getName());
    270             buf.append("] ");
    271         }
    272
    273         if (CONFIG_PARAMS.levelInBrackets)
    274             buf.append('[');
    275
    276         // Append a readable representation of the log level
    277         String levelStr = renderLevel(level);
    278         buf.append(levelStr);
    279         if (CONFIG_PARAMS.levelInBrackets)
    280             buf.append(']');
    281         buf.append(' ');
    282
    283         // Append the name of the log instance if so configured
    284         if (CONFIG_PARAMS.showShortLogName) {
    285             if (shortLogName == null)
    286                 shortLogName = computeShortName();
    287             buf.append(String.valueOf(shortLogName)).append(" - ");
    288         } else if (CONFIG_PARAMS.showLogName) {
    289             buf.append(String.valueOf(name)).append(" - ");
    290         }
    291
    292         // Append the message
    293         buf.append(message);
    294
    295         write(buf, t);
    296
    297     }
    298
    299     protected String renderLevel(int level) {
    300         switch (level) {
    301         case LOG_LEVEL_TRACE:
    302             return "TRACE";
    303         case LOG_LEVEL_DEBUG:
    304             return ("DEBUG");
    305         case LOG_LEVEL_INFO:
    306             return "INFO";
    307         case LOG_LEVEL_WARN:
    308             return CONFIG_PARAMS.warnLevelString;
    309         case LOG_LEVEL_ERROR:
    310             return "ERROR";
    311         }
    312         throw new IllegalStateException("Unrecognized level [" + level + "]");
    313     }
    314
    315     void write(StringBuilder buf, Throwable t) {
    316         PrintStream targetStream = CONFIG_PARAMS.outputChoice.getTargetPrintStream();
    317
    318         targetStream.println(buf.toString());
    319         writeThrowable(t, targetStream);
    320         targetStream.flush();
    321     }
    322
    323     protected void writeThrowable(Throwable t, PrintStream targetStream) {
    324         if (t != null) {
    325             t.printStackTrace(targetStream);
    326         }
    327     }
    328
    329     private String getFormattedDate() {
    330         Date now = new Date();
    331         String dateText;
    332         synchronized (CONFIG_PARAMS.dateFormatter) {
    333             dateText = CONFIG_PARAMS.dateFormatter.format(now);
    334         }
    335         return dateText;
    336     }
    337
    338     private String computeShortName() {
    339         return name.substring(name.lastIndexOf(".") + 1);
    340     }
    341
    342     /**
    343      * For formatted messages, first substitute arguments and then log.
    344      *
    345      * @param level
    346      * @param format
    347      * @param arg1
    348      * @param arg2
    349      */
    350     private void formatAndLog(int level, String format, Object arg1, Object arg2) {
    351         if (!isLevelEnabled(level)) {
    352             return;
    353         }
    354         FormattingTuple tp = MessageFormatter.format(format, arg1, arg2);
    355         log(level, tp.getMessage(), tp.getThrowable());
    356     }
    357
    358     /**
    359      * For formatted messages, first substitute arguments and then log.
    360      *
    361      * @param level
    362      * @param format
    363      * @param arguments
    364      *            a list of 3 ore more arguments
    365      */
    366     private void formatAndLog(int level, String format, Object... arguments) {
    367         if (!isLevelEnabled(level)) {
    368             return;
    369         }
    370         FormattingTuple tp = MessageFormatter.arrayFormat(format, arguments);
    371         log(level, tp.getMessage(), tp.getThrowable());
    372     }
    373
    374     /**
    375      * Is the given log level currently enabled?
    376      *
    377      * @param logLevel
    378      *            is this level enabled?
    379      */
    380     protected boolean isLevelEnabled(int logLevel) {
    381         // log level are numerically ordered so can use simple numeric
    382         // comparison
    383         return (logLevel >= currentLogLevel);
    384     }
    385
    386     /** Are {@code trace} messages currently enabled? */
    387     public boolean isTraceEnabled() {
    388         return isLevelEnabled(LOG_LEVEL_TRACE);
    389     }
    390
    391     /**
    392      * A simple implementation which logs messages of level TRACE according to
    393      * the format outlined above.
    394      */
    395     public void trace(String msg) {
    396         log(LOG_LEVEL_TRACE, msg, null);
    397     }
    398
    399     /**
    400      * Perform single parameter substitution before logging the message of level
    401      * TRACE according to the format outlined above.
    402      */
    403     public void trace(String format, Object param1) {
    404         formatAndLog(LOG_LEVEL_TRACE, format, param1, null);
    405     }
    406
    407     /**
    408      * Perform double parameter substitution before logging the message of level
    409      * TRACE according to the format outlined above.
    410      */
    411     public void trace(String format, Object param1, Object param2) {
    412         formatAndLog(LOG_LEVEL_TRACE, format, param1, param2);
    413     }
    414
    415     /**
    416      * Perform double parameter substitution before logging the message of level
    417      * TRACE according to the format outlined above.
    418      */
    419     public void trace(String format, Object... argArray) {
    420         formatAndLog(LOG_LEVEL_TRACE, format, argArray);
    421     }
    422
    423     /** Log a message of level TRACE, including an exception. */
    424     public void trace(String msg, Throwable t) {
    425         log(LOG_LEVEL_TRACE, msg, t);
    426     }
    427
    428     /** Are {@code debug} messages currently enabled? */
    429     public boolean isDebugEnabled() {
    430         return isLevelEnabled(LOG_LEVEL_DEBUG);
    431     }
    432
    433     /**
    434      * A simple implementation which logs messages of level DEBUG according to
    435      * the format outlined above.
    436      */
    437     public void debug(String msg) {
    438         log(LOG_LEVEL_DEBUG, msg, null);
    439     }
    440
    441     /**
    442      * Perform single parameter substitution before logging the message of level
    443      * DEBUG according to the format outlined above.
    444      */
    445     public void debug(String format, Object param1) {
    446         formatAndLog(LOG_LEVEL_DEBUG, format, param1, null);
    447     }
    448
    449     /**
    450      * Perform double parameter substitution before logging the message of level
    451      * DEBUG according to the format outlined above.
    452      */
    453     public void debug(String format, Object param1, Object param2) {
    454         formatAndLog(LOG_LEVEL_DEBUG, format, param1, param2);
    455     }
    456
    457     /**
    458      * Perform double parameter substitution before logging the message of level
    459      * DEBUG according to the format outlined above.
    460      */
    461     public void debug(String format, Object... argArray) {
    462         formatAndLog(LOG_LEVEL_DEBUG, format, argArray);
    463     }
    464
    465     /** Log a message of level DEBUG, including an exception. */
    466     public void debug(String msg, Throwable t) {
    467         log(LOG_LEVEL_DEBUG, msg, t);
    468     }
    469
    470     /** Are {@code info} messages currently enabled? */
    471     public boolean isInfoEnabled() {
    472         return isLevelEnabled(LOG_LEVEL_INFO);
    473     }
    474
    475     /**
    476      * A simple implementation which logs messages of level INFO according to
    477      * the format outlined above.
    478      */
    479     public void info(String msg) {
    480         log(LOG_LEVEL_INFO, msg, null);
    481     }
    482
    483     /**
    484      * Perform single parameter substitution before logging the message of level
    485      * INFO according to the format outlined above.
    486      */
    487     public void info(String format, Object arg) {
    488         formatAndLog(LOG_LEVEL_INFO, format, arg, null);
    489     }
    490
    491     /**
    492      * Perform double parameter substitution before logging the message of level
    493      * INFO according to the format outlined above.
    494      */
    495     public void info(String format, Object arg1, Object arg2) {
    496         formatAndLog(LOG_LEVEL_INFO, format, arg1, arg2);
    497     }
    498
    499     /**
    500      * Perform double parameter substitution before logging the message of level
    501      * INFO according to the format outlined above.
    502      */
    503     public void info(String format, Object... argArray) {
    504         formatAndLog(LOG_LEVEL_INFO, format, argArray);
    505     }
    506
    507     /** Log a message of level INFO, including an exception. */
    508     public void info(String msg, Throwable t) {
    509         log(LOG_LEVEL_INFO, msg, t);
    510     }
    511
    512     /** Are {@code warn} messages currently enabled? */
    513     public boolean isWarnEnabled() {
    514         return isLevelEnabled(LOG_LEVEL_WARN);
    515     }
    516
    517     /**
    518      * A simple implementation which always logs messages of level WARN
    519      * according to the format outlined above.
    520      */
    521     public void warn(String msg) {
    522         log(LOG_LEVEL_WARN, msg, null);
    523     }
    524
    525     /**
    526      * Perform single parameter substitution before logging the message of level
    527      * WARN according to the format outlined above.
    528      */
    529     public void warn(String format, Object arg) {
    530         formatAndLog(LOG_LEVEL_WARN, format, arg, null);
    531     }
    532
    533     /**
    534      * Perform double parameter substitution before logging the message of level
    535      * WARN according to the format outlined above.
    536      */
    537     public void warn(String format, Object arg1, Object arg2) {
    538         formatAndLog(LOG_LEVEL_WARN, format, arg1, arg2);
    539     }
    540
    541     /**
    542      * Perform double parameter substitution before logging the message of level
    543      * WARN according to the format outlined above.
    544      */
    545     public void warn(String format, Object... argArray) {
    546         formatAndLog(LOG_LEVEL_WARN, format, argArray);
    547     }
    548
    549     /** Log a message of level WARN, including an exception. */
    550     public void warn(String msg, Throwable t) {
    551         log(LOG_LEVEL_WARN, msg, t);
    552     }
    553
    554     /** Are {@code error} messages currently enabled? */
    555     public boolean isErrorEnabled() {
    556         return isLevelEnabled(LOG_LEVEL_ERROR);
    557     }
    558
    559     /**
    560      * A simple implementation which always logs messages of level ERROR
    561      * according to the format outlined above.
    562      */
    563     public void error(String msg) {
    564         log(LOG_LEVEL_ERROR, msg, null);
    565     }
    566
    567     /**
    568      * Perform single parameter substitution before logging the message of level
    569      * ERROR according to the format outlined above.
    570      */
    571     public void error(String format, Object arg) {
    572         formatAndLog(LOG_LEVEL_ERROR, format, arg, null);
    573     }
    574
    575     /**
    576      * Perform double parameter substitution before logging the message of level
    577      * ERROR according to the format outlined above.
    578      */
    579     public void error(String format, Object arg1, Object arg2) {
    580         formatAndLog(LOG_LEVEL_ERROR, format, arg1, arg2);
    581     }
    582
    583     /**
    584      * Perform double parameter substitution before logging the message of level
    585      * ERROR according to the format outlined above.
    586      */
    587     public void error(String format, Object... argArray) {
    588         formatAndLog(LOG_LEVEL_ERROR, format, argArray);
    589     }
    590
    591     /** Log a message of level ERROR, including an exception. */
    592     public void error(String msg, Throwable t) {
    593         log(LOG_LEVEL_ERROR, msg, t);
    594     }
    595
    596     public void log(LoggingEvent event) {
    597         int levelInt = event.getLevel().toInt();
    598
    599         if (!isLevelEnabled(levelInt)) {
    600             return;
    601         }
    602         FormattingTuple tp = MessageFormatter.arrayFormat(event.getMessage(), event.getArgumentArray(), event.getThrowable());
    603         log(levelInt, tp.getMessage(), event.getThrowable());
    604     }
    605
    606 }