001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.logging.impl;
019    
020    import java.io.InputStream;
021    import java.io.Serializable;
022    import java.lang.reflect.InvocationTargetException;
023    import java.lang.reflect.Method;
024    import java.security.AccessController;
025    import java.security.PrivilegedAction;
026    import java.text.DateFormat;
027    import java.text.SimpleDateFormat;
028    import java.util.Date;
029    import java.util.Properties;
030    
031    import org.apache.commons.logging.Log;
032    import org.apache.commons.logging.LogConfigurationException;
033    
034    /**
035     * Simple implementation of Log that sends all enabled log messages,
036     * for all defined loggers, to System.err.  The following system properties
037     * are supported to configure the behavior of this logger:
038     * <ul>
039     * <li><code>org.apache.commons.logging.simplelog.defaultlog</code> -
040     *     Default logging detail level for all instances of SimpleLog.
041     *     Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").
042     *     If not specified, defaults to "info". </li>
043     * <li><code>org.apache.commons.logging.simplelog.log.xxxxx</code> -
044     *     Logging detail level for a SimpleLog instance named "xxxxx".
045     *     Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").
046     *     If not specified, the default logging detail level is used.</li>
047     * <li><code>org.apache.commons.logging.simplelog.showlogname</code> -
048     *     Set to <code>true</code> if you want the Log instance name to be
049     *     included in output messages. Defaults to <code>false</code>.</li>
050     * <li><code>org.apache.commons.logging.simplelog.showShortLogname</code> -
051     *     Set to <code>true</code> if you want the last component of the name to be
052     *     included in output messages. Defaults to <code>true</code>.</li>
053     * <li><code>org.apache.commons.logging.simplelog.showdatetime</code> -
054     *     Set to <code>true</code> if you want the current date and time
055     *     to be included in output messages. Default is <code>false</code>.</li>
056     * <li><code>org.apache.commons.logging.simplelog.dateTimeFormat</code> -
057     *     The date and time format to be used in the output messages.
058     *     The pattern describing the date and time format is the same that is
059     *     used in <code>java.text.SimpleDateFormat</code>. If the format is not
060     *     specified or is invalid, the default format is used.
061     *     The default format is <code>yyyy/MM/dd HH:mm:ss:SSS zzz</code>.</li>
062     * </ul>
063     * <p>
064     * In addition to looking for system properties with the names specified
065     * above, this implementation also checks for a class loader resource named
066     * <code>"simplelog.properties"</code>, and includes any matching definitions
067     * from this resource (if it exists).
068     *
069     * @version $Id: SimpleLog.html 862526 2013-05-20 17:07:42Z tn $
070     */
071    public class SimpleLog implements Log, Serializable {
072    
073        /** Serializable version identifier. */
074        private static final long serialVersionUID = 136942970684951178L;
075    
076        // ------------------------------------------------------- Class Attributes
077    
078        /** All system properties used by <code>SimpleLog</code> start with this */
079        static protected final String systemPrefix = "org.apache.commons.logging.simplelog.";
080    
081        /** Properties loaded from simplelog.properties */
082        static protected final Properties simpleLogProps = new Properties();
083    
084        /** The default format to use when formating dates */
085        static protected final String DEFAULT_DATE_TIME_FORMAT = "yyyy/MM/dd HH:mm:ss:SSS zzz";
086    
087        /** Include the instance name in the log message? */
088        static volatile protected boolean showLogName = false;
089    
090        /** Include the short name ( last component ) of the logger in the log
091         *  message. Defaults to true - otherwise we'll be lost in a flood of
092         *  messages without knowing who sends them.
093         */
094        static volatile protected boolean showShortName = true;
095    
096        /** Include the current time in the log message */
097        static volatile protected boolean showDateTime = false;
098    
099        /** The date and time format to use in the log message */
100        static volatile protected String dateTimeFormat = DEFAULT_DATE_TIME_FORMAT;
101    
102        /**
103         * Used to format times.
104         * <p>
105         * Any code that accesses this object should first obtain a lock on it,
106         * ie use synchronized(dateFormatter); this requirement was introduced
107         * in 1.1.1 to fix an existing thread safety bug (SimpleDateFormat.format
108         * is not thread-safe).
109         */
110        static protected DateFormat dateFormatter = null;
111    
112        // ---------------------------------------------------- Log Level Constants
113    
114        /** "Trace" level logging. */
115        public static final int LOG_LEVEL_TRACE  = 1;
116        /** "Debug" level logging. */
117        public static final int LOG_LEVEL_DEBUG  = 2;
118        /** "Info" level logging. */
119        public static final int LOG_LEVEL_INFO   = 3;
120        /** "Warn" level logging. */
121        public static final int LOG_LEVEL_WARN   = 4;
122        /** "Error" level logging. */
123        public static final int LOG_LEVEL_ERROR  = 5;
124        /** "Fatal" level logging. */
125        public static final int LOG_LEVEL_FATAL  = 6;
126    
127        /** Enable all logging levels */
128        public static final int LOG_LEVEL_ALL    = LOG_LEVEL_TRACE - 1;
129    
130        /** Enable no logging levels */
131        public static final int LOG_LEVEL_OFF    = LOG_LEVEL_FATAL + 1;
132    
133        // ------------------------------------------------------------ Initializer
134    
135        private static String getStringProperty(String name) {
136            String prop = null;
137            try {
138                prop = System.getProperty(name);
139            } catch (SecurityException e) {
140                // Ignore
141            }
142            return prop == null ? simpleLogProps.getProperty(name) : prop;
143        }
144    
145        private static String getStringProperty(String name, String dephault) {
146            String prop = getStringProperty(name);
147            return prop == null ? dephault : prop;
148        }
149    
150        private static boolean getBooleanProperty(String name, boolean dephault) {
151            String prop = getStringProperty(name);
152            return prop == null ? dephault : "true".equalsIgnoreCase(prop);
153        }
154    
155        // Initialize class attributes.
156        // Load properties file, if found.
157        // Override with system properties.
158        static {
159            // Add props from the resource simplelog.properties
160            InputStream in = getResourceAsStream("simplelog.properties");
161            if(null != in) {
162                try {
163                    simpleLogProps.load(in);
164                    in.close();
165                } catch(java.io.IOException e) {
166                    // ignored
167                }
168            }
169    
170            showLogName = getBooleanProperty(systemPrefix + "showlogname", showLogName);
171            showShortName = getBooleanProperty(systemPrefix + "showShortLogname", showShortName);
172            showDateTime = getBooleanProperty(systemPrefix + "showdatetime", showDateTime);
173    
174            if(showDateTime) {
175                dateTimeFormat = getStringProperty(systemPrefix + "dateTimeFormat",
176                                                   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 volatile String logName = null;
191        /** The current log level */
192        protected volatile int currentLogLevel;
193        /** The short name of this simple log instance */
194        private volatile String shortLogName = null;
195    
196        // ------------------------------------------------------------ Constructor
197    
198        /**
199         * Construct a simple log with given name.
200         *
201         * @param name log name
202         */
203        public SimpleLog(String name) {
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        // -------------------------------------------------------- Properties
244    
245        /**
246         * Set logging level.
247         *
248         * @param currentLogLevel new logging level
249         */
250        public void setLevel(int currentLogLevel) {
251            this.currentLogLevel = currentLogLevel;
252        }
253    
254        /**
255         * Get logging level.
256         */
257        public int getLevel() {
258            return currentLogLevel;
259        }
260    
261        // -------------------------------------------------------- Logging Methods
262    
263        /**
264         * Do the actual logging.
265         * <p>
266         * This method assembles the message and then calls <code>write()</code>
267         * to cause it to be written.
268         *
269         * @param type One of the LOG_LEVEL_XXX constants defining the log level
270         * @param message The message itself (typically a String)
271         * @param t The exception whose stack trace should be logged
272         */
273        protected void log(int type, Object message, Throwable t) {
274            // Use a string buffer for better performance
275            final StringBuffer buf = new StringBuffer();
276    
277            // Append date-time if so configured
278            if(showDateTime) {
279                final Date now = new Date();
280                String dateText;
281                synchronized(dateFormatter) {
282                    dateText = dateFormatter.format(now);
283                }
284                buf.append(dateText);
285                buf.append(" ");
286            }
287    
288            // Append a readable representation of the log level
289            switch(type) {
290                case SimpleLog.LOG_LEVEL_TRACE: buf.append("[TRACE] "); break;
291                case SimpleLog.LOG_LEVEL_DEBUG: buf.append("[DEBUG] "); break;
292                case SimpleLog.LOG_LEVEL_INFO:  buf.append("[INFO] ");  break;
293                case SimpleLog.LOG_LEVEL_WARN:  buf.append("[WARN] ");  break;
294                case SimpleLog.LOG_LEVEL_ERROR: buf.append("[ERROR] "); break;
295                case SimpleLog.LOG_LEVEL_FATAL: buf.append("[FATAL] "); break;
296            }
297    
298            // Append the name of the log instance if so configured
299            if(showShortName) {
300                if(shortLogName == null) {
301                    // Cut all but the last component of the name for both styles
302                    final String slName = logName.substring(logName.lastIndexOf(".") + 1);
303                    shortLogName = slName.substring(slName.lastIndexOf("/") + 1);
304                }
305                buf.append(String.valueOf(shortLogName)).append(" - ");
306            } else if(showLogName) {
307                buf.append(String.valueOf(logName)).append(" - ");
308            }
309    
310            // Append the message
311            buf.append(String.valueOf(message));
312    
313            // Append stack trace if not null
314            if(t != null) {
315                buf.append(" <");
316                buf.append(t.toString());
317                buf.append(">");
318    
319                final java.io.StringWriter sw = new java.io.StringWriter(1024);
320                final java.io.PrintWriter pw = new java.io.PrintWriter(sw);
321                t.printStackTrace(pw);
322                pw.close();
323                buf.append(sw.toString());
324            }
325    
326            // Print to the appropriate destination
327            write(buf);
328        }
329    
330        /**
331         * Write the content of the message accumulated in the specified
332         * <code>StringBuffer</code> to the appropriate output destination.  The
333         * default implementation writes to <code>System.err</code>.
334         *
335         * @param buffer A <code>StringBuffer</code> containing the accumulated
336         *  text to be logged
337         */
338        protected void write(StringBuffer buffer) {
339            System.err.println(buffer.toString());
340        }
341    
342        /**
343         * Is the given log level currently enabled?
344         *
345         * @param logLevel is this level enabled?
346         */
347        protected boolean isLevelEnabled(int logLevel) {
348            // log level are numerically ordered so can use simple numeric
349            // comparison
350            return logLevel >= currentLogLevel;
351        }
352    
353        // -------------------------------------------------------- Log Implementation
354    
355        /**
356         * Logs a message with
357         * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_DEBUG</code>.
358         *
359         * @param message to log
360         * @see org.apache.commons.logging.Log#debug(Object)
361         */
362        public final void debug(Object message) {
363            if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) {
364                log(SimpleLog.LOG_LEVEL_DEBUG, message, null);
365            }
366        }
367    
368        /**
369         * Logs a message with
370         * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_DEBUG</code>.
371         *
372         * @param message to log
373         * @param t log this cause
374         * @see org.apache.commons.logging.Log#debug(Object, Throwable)
375         */
376        public final void debug(Object message, Throwable t) {
377            if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) {
378                log(SimpleLog.LOG_LEVEL_DEBUG, message, t);
379            }
380        }
381    
382        /**
383         * Logs a message with <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_TRACE</code>.
384         *
385         * @param message to log
386         * @see org.apache.commons.logging.Log#trace(Object)
387         */
388        public final void trace(Object message) {
389            if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) {
390                log(SimpleLog.LOG_LEVEL_TRACE, message, null);
391            }
392        }
393    
394        /**
395         * Logs a message with <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_TRACE</code>.
396         *
397         * @param message to log
398         * @param t log this cause
399         * @see org.apache.commons.logging.Log#trace(Object, Throwable)
400         */
401        public final void trace(Object message, Throwable t) {
402            if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) {
403                log(SimpleLog.LOG_LEVEL_TRACE, message, t);
404            }
405        }
406    
407        /**
408         * Logs a message with <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_INFO</code>.
409         *
410         * @param message to log
411         * @see org.apache.commons.logging.Log#info(Object)
412         */
413        public final void info(Object message) {
414            if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) {
415                log(SimpleLog.LOG_LEVEL_INFO,message,null);
416            }
417        }
418    
419        /**
420         * Logs a message with <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_INFO</code>.
421         *
422         * @param message to log
423         * @param t log this cause
424         * @see org.apache.commons.logging.Log#info(Object, Throwable)
425         */
426        public final void info(Object message, Throwable t) {
427            if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) {
428                log(SimpleLog.LOG_LEVEL_INFO, message, t);
429            }
430        }
431    
432        /**
433         * Logs a message with <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_WARN</code>.
434         *
435         * @param message to log
436         * @see org.apache.commons.logging.Log#warn(Object)
437         */
438        public final void warn(Object message) {
439            if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) {
440                log(SimpleLog.LOG_LEVEL_WARN, message, null);
441            }
442        }
443    
444        /**
445         * Logs a message with <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_WARN</code>.
446         *
447         * @param message to log
448         * @param t log this cause
449         * @see org.apache.commons.logging.Log#warn(Object, Throwable)
450         */
451        public final void warn(Object message, Throwable t) {
452            if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) {
453                log(SimpleLog.LOG_LEVEL_WARN, message, t);
454            }
455        }
456    
457        /**
458         * Logs a message with <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_ERROR</code>.
459         *
460         * @param message to log
461         * @see org.apache.commons.logging.Log#error(Object)
462         */
463        public final void error(Object message) {
464            if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) {
465                log(SimpleLog.LOG_LEVEL_ERROR, message, null);
466            }
467        }
468    
469        /**
470         * Logs a message with <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_ERROR</code>.
471         *
472         * @param message to log
473         * @param t log this cause
474         * @see org.apache.commons.logging.Log#error(Object, Throwable)
475         */
476        public final void error(Object message, Throwable t) {
477            if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) {
478                log(SimpleLog.LOG_LEVEL_ERROR, message, t);
479            }
480        }
481    
482        /**
483         * Log a message with <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_FATAL</code>.
484         *
485         * @param message to log
486         * @see org.apache.commons.logging.Log#fatal(Object)
487         */
488        public final void fatal(Object message) {
489            if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) {
490                log(SimpleLog.LOG_LEVEL_FATAL, message, null);
491            }
492        }
493    
494        /**
495         * Logs a message with <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_FATAL</code>.
496         *
497         * @param message to log
498         * @param t log this cause
499         * @see org.apache.commons.logging.Log#fatal(Object, Throwable)
500         */
501        public final void fatal(Object message, Throwable t) {
502            if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) {
503                log(SimpleLog.LOG_LEVEL_FATAL, message, t);
504            }
505        }
506    
507        /**
508         * Are debug messages currently enabled?
509         * <p>
510         * This allows expensive operations such as <code>String</code>
511         * concatenation to be avoided when the message will be ignored by the
512         * logger.
513         */
514        public final boolean isDebugEnabled() {
515            return isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG);
516        }
517    
518        /**
519         * Are error messages currently enabled?
520         * <p>
521         * This allows expensive operations such as <code>String</code>
522         * concatenation to be avoided when the message will be ignored by the
523         * logger.
524         */
525        public final boolean isErrorEnabled() {
526            return isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR);
527        }
528    
529        /**
530         * Are fatal messages currently enabled?
531         * <p>
532         * This allows expensive operations such as <code>String</code>
533         * concatenation to be avoided when the message will be ignored by the
534         * logger.
535         */
536        public final boolean isFatalEnabled() {
537            return isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL);
538        }
539    
540        /**
541         * Are info messages currently enabled?
542         * <p>
543         * This allows expensive operations such as <code>String</code>
544         * concatenation to be avoided when the message will be ignored by the
545         * logger.
546         */
547        public final boolean isInfoEnabled() {
548            return isLevelEnabled(SimpleLog.LOG_LEVEL_INFO);
549        }
550    
551        /**
552         * Are trace messages currently enabled?
553         * <p>
554         * This allows expensive operations such as <code>String</code>
555         * concatenation to be avoided when the message will be ignored by the
556         * logger.
557         */
558        public final boolean isTraceEnabled() {
559            return isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE);
560        }
561    
562        /**
563         * Are warn messages currently enabled?
564         * <p>
565         * This allows expensive operations such as <code>String</code>
566         * concatenation to be avoided when the message will be ignored by the
567         * logger.
568         */
569        public final boolean isWarnEnabled() {
570            return isLevelEnabled(SimpleLog.LOG_LEVEL_WARN);
571        }
572    
573        /**
574         * Return the thread context class loader if available.
575         * Otherwise return null.
576         *
577         * The thread context class loader is available for JDK 1.2
578         * or later, if certain security conditions are met.
579         *
580         * @exception LogConfigurationException if a suitable class loader
581         * cannot be identified.
582         */
583        private static ClassLoader getContextClassLoader() {
584            ClassLoader classLoader = null;
585    
586            try {
587                // Are we running on a JDK 1.2 or later system?
588                final Method method = Thread.class.getMethod("getContextClassLoader", (Class[]) null);
589    
590                // Get the thread context class loader (if there is one)
591                try {
592                    classLoader = (ClassLoader)method.invoke(Thread.currentThread(), (Class[]) null);
593                } catch (IllegalAccessException e) {
594                    // ignore
595                } catch (InvocationTargetException e) {
596                    /**
597                     * InvocationTargetException is thrown by 'invoke' when
598                     * the method being invoked (getContextClassLoader) throws
599                     * an exception.
600                     *
601                     * getContextClassLoader() throws SecurityException when
602                     * the context class loader isn't an ancestor of the
603                     * calling class's class loader, or if security
604                     * permissions are restricted.
605                     *
606                     * In the first case (not related), we want to ignore and
607                     * keep going.  We cannot help but also ignore the second
608                     * with the logic below, but other calls elsewhere (to
609                     * obtain a class loader) will trigger this exception where
610                     * we can make a distinction.
611                     */
612                    if (e.getTargetException() instanceof SecurityException) {
613                        // ignore
614                    } else {
615                        // Capture 'e.getTargetException()' exception for details
616                        // alternate: log 'e.getTargetException()', and pass back 'e'.
617                        throw new LogConfigurationException
618                            ("Unexpected InvocationTargetException", e.getTargetException());
619                    }
620                }
621            } catch (NoSuchMethodException e) {
622                // Assume we are running on JDK 1.1
623                // ignore
624            }
625    
626            if (classLoader == null) {
627                classLoader = SimpleLog.class.getClassLoader();
628            }
629    
630            // Return the selected class loader
631            return classLoader;
632        }
633    
634        private static InputStream getResourceAsStream(final String name) {
635            return (InputStream)AccessController.doPrivileged(
636                new PrivilegedAction() {
637                    public Object run() {
638                        ClassLoader threadCL = getContextClassLoader();
639    
640                        if (threadCL != null) {
641                            return threadCL.getResourceAsStream(name);
642                        } else {
643                            return ClassLoader.getSystemResourceAsStream(name);
644                        }
645                    }
646                });
647        }
648    }
649