View Javadoc

    1   /*
    2    * Copyright 2001-2004 The Apache Software Foundation.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16
    17  package org.apache.commons.logging.impl;
    18
    19  import java.io.InputStream;
    20  import java.io.Serializable;
    21  import java.lang.reflect.InvocationTargetException;
    22  import java.lang.reflect.Method;
    23  import java.security.AccessController;
    24  import java.security.PrivilegedAction;
    25  import java.text.DateFormat;
    26  import java.text.SimpleDateFormat;
    27  import java.util.Date;
    28  import java.util.Properties;
    29
    30  import org.apache.commons.logging.Log;
    31  import org.apache.commons.logging.LogConfigurationException;
    32
    33  /**
    34   * <p>
    35   * Simple implementation of Log that sends all enabled log messages, for all
    36   * defined loggers, to System.err. The following system properties are supported
    37   * to configure the behavior of this logger:
    38   * </p>
    39   * <ul>
    40   * <li><code>org.apache.commons.logging.simplelog.defaultlog</code> - Default
    41   * logging detail level for all instances of SimpleLog. Must be one of ("trace",
    42   * "debug", "info", "warn", "error", or "fatal"). If not specified, defaults to
    43   * "info".</li>
    44   * <li><code>org.apache.commons.logging.simplelog.log.xxxxx</code> - Logging
    45   * detail level for a SimpleLog instance named "xxxxx". Must be one of ("trace",
    46   * "debug", "info", "warn", "error", or "fatal"). If not specified, the default
    47   * logging detail level is used.</li>
    48   * <li><code>org.apache.commons.logging.simplelog.showlogname</code> - Set to
    49   * <code>true</code> if you want the Log instance name to be included in output
    50   * messages. Defaults to <code>false</code>.</li>
    51   * <li><code>org.apache.commons.logging.simplelog.showShortLogname</code> - Set
    52   * to <code>true</code> if you want the last component of the name to be
    53   * included in output messages. Defaults to <code>true</code>.</li>
    54   * <li><code>org.apache.commons.logging.simplelog.showdatetime</code> - Set to
    55   * <code>true</code> if you want the current date and time to be included in
    56   * output messages. Default is <code>false</code>.</li>
    57   * <li><code>org.apache.commons.logging.simplelog.dateTimeFormat</code> - The
    58   * date and time format to be used in the output messages. The pattern
    59   * describing the date and time format is the same that is used in
    60   * <code>java.text.SimpleDateFormat</code>. If the format is not specified or is
    61   * invalid, the default format is used. The default format is
    62   * <code>yyyy/MM/dd HH:mm:ss:SSS zzz</code>.</li>
    63   * </ul>
    64   *
    65   * <p>
    66   * In addition to looking for system properties with the names specified above,
    67   * this implementation also checks for a class loader resource named
    68   * <code>"simplelog.properties"</code>, and includes any matching definitions
    69   * from this resource (if it exists).
    70   * </p>
    71   *
    72   * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
    73   * @author Rod Waldhoff
    74   * @author Robert Burrell Donkin
    75   *
    76   * @version $Id: SimpleLog.java,v 1.21 2004/06/06 20:47:56 rdonkin Exp $
    77   */
    78  public class SimpleLog implements Log, Serializable {
    79
    80      private static final long serialVersionUID = 136942970684951178L;
    81
    82      // ------------------------------------------------------- Class Attributes
    83
    84      /** All system properties used by <code>SimpleLog</code> start with this */
    85      static protected final String systemPrefix = "org.apache.commons.logging.simplelog.";
    86
    87      /** Properties loaded from simplelog.properties */
    88      static protected final Properties simpleLogProps = new Properties();
    89
    90      /** The default format to use when 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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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      * </p>
    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(new PrivilegedAction<InputStream>() {
    677             public InputStream run() {
    678                 ClassLoader threadCL = getContextClassLoader();
    679
    680                 if (threadCL != null) {
    681                     return threadCL.getResourceAsStream(name);
    682                 } else {
    683                     return ClassLoader.getSystemResourceAsStream(name);
    684                 }
    685             }
    686         });
    687     }
    688 }