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