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