SimpleLog xref

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