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,v 1.21 2004/06/06 20:47:56 rdonkin Exp $
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: * <p> Log a message with debug log level.</p>
367: */
368: public final void debug(Object message) {
369:
370: if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) {
371: log(SimpleLog.LOG_LEVEL_DEBUG, message, null);
372: }
373: }
374:
375: /**
376: * <p> Log an error with debug log level.</p>
377: */
378: public final void debug(Object message, Throwable t) {
379:
380: if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) {
381: log(SimpleLog.LOG_LEVEL_DEBUG, message, t);
382: }
383: }
384:
385: /**
386: * <p> Log a message with trace log level.</p>
387: */
388: public final void trace(Object message) {
389:
390: if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) {
391: log(SimpleLog.LOG_LEVEL_TRACE, message, null);
392: }
393: }
394:
395: /**
396: * <p> Log an error with trace log level.</p>
397: */
398: public final void trace(Object message, Throwable t) {
399:
400: if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) {
401: log(SimpleLog.LOG_LEVEL_TRACE, message, t);
402: }
403: }
404:
405: /**
406: * <p> Log a message with info log level.</p>
407: */
408: public final void info(Object message) {
409:
410: if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) {
411: log(SimpleLog.LOG_LEVEL_INFO, message, null);
412: }
413: }
414:
415: /**
416: * <p> Log an error with info log level.</p>
417: */
418: public final void info(Object message, Throwable t) {
419:
420: if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) {
421: log(SimpleLog.LOG_LEVEL_INFO, message, t);
422: }
423: }
424:
425: /**
426: * <p> Log a message with warn log level.</p>
427: */
428: public final void warn(Object message) {
429:
430: if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) {
431: log(SimpleLog.LOG_LEVEL_WARN, message, null);
432: }
433: }
434:
435: /**
436: * <p> Log an error with warn log level.</p>
437: */
438: public final void warn(Object message, Throwable t) {
439:
440: if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) {
441: log(SimpleLog.LOG_LEVEL_WARN, message, t);
442: }
443: }
444:
445: /**
446: * <p> Log a message with error log level.</p>
447: */
448: public final void error(Object message) {
449:
450: if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) {
451: log(SimpleLog.LOG_LEVEL_ERROR, message, null);
452: }
453: }
454:
455: /**
456: * <p> Log an error with error log level.</p>
457: */
458: public final void error(Object message, Throwable t) {
459:
460: if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) {
461: log(SimpleLog.LOG_LEVEL_ERROR, message, t);
462: }
463: }
464:
465: /**
466: * <p> Log a message with fatal log level.</p>
467: */
468: public final void fatal(Object message) {
469:
470: if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) {
471: log(SimpleLog.LOG_LEVEL_FATAL, message, null);
472: }
473: }
474:
475: /**
476: * <p> Log an error with fatal log level.</p>
477: */
478: public final void fatal(Object message, Throwable t) {
479:
480: if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) {
481: log(SimpleLog.LOG_LEVEL_FATAL, message, t);
482: }
483: }
484:
485: /**
486: * <p> Are debug messages currently enabled? </p>
487: *
488: * <p> This allows expensive operations such as <code>String</code>
489: * concatenation to be avoided when the message will be ignored by the
490: * logger. </p>
491: */
492: public final boolean isDebugEnabled() {
493:
494: return isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG);
495: }
496:
497: /**
498: * <p> Are error messages currently enabled? </p>
499: *
500: * <p> This allows expensive operations such as <code>String</code>
501: * concatenation to be avoided when the message will be ignored by the
502: * logger. </p>
503: */
504: public final boolean isErrorEnabled() {
505:
506: return isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR);
507: }
508:
509: /**
510: * <p> Are fatal messages currently enabled? </p>
511: *
512: * <p> This allows expensive operations such as <code>String</code>
513: * concatenation to be avoided when the message will be ignored by the
514: * logger. </p>
515: */
516: public final boolean isFatalEnabled() {
517:
518: return isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL);
519: }
520:
521: /**
522: * <p> Are info messages currently enabled? </p>
523: *
524: * <p> This allows expensive operations such as <code>String</code>
525: * concatenation to be avoided when the message will be ignored by the
526: * logger. </p>
527: */
528: public final boolean isInfoEnabled() {
529:
530: return isLevelEnabled(SimpleLog.LOG_LEVEL_INFO);
531: }
532:
533: /**
534: * <p> Are trace messages currently enabled? </p>
535: *
536: * <p> This allows expensive operations such as <code>String</code>
537: * concatenation to be avoided when the message will be ignored by the
538: * logger. </p>
539: */
540: public final boolean isTraceEnabled() {
541:
542: return isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE);
543: }
544:
545: /**
546: * <p> Are warn messages currently enabled? </p>
547: *
548: * <p> This allows expensive operations such as <code>String</code>
549: * concatenation to be avoided when the message will be ignored by the
550: * logger. </p>
551: */
552: public final boolean isWarnEnabled() {
553:
554: return isLevelEnabled(SimpleLog.LOG_LEVEL_WARN);
555: }
556:
557: /**
558: * Return the thread context class loader if available.
559: * Otherwise return null.
560: *
561: * The thread context class loader is available for JDK 1.2
562: * or later, if certain security conditions are met.
563: *
564: * @exception LogConfigurationException if a suitable class loader
565: * cannot be identified.
566: */
567: private static ClassLoader getContextClassLoader() {
568: ClassLoader classLoader = null;
569:
570: if (classLoader == null) {
571: try {
572: // Are we running on a JDK 1.2 or later system?
573: Method method = Thread.class.getMethod(
574: "getContextClassLoader", null);
575:
576: // Get the thread context class loader (if there is one)
577: try {
578: classLoader = (ClassLoader) method.invoke(Thread
579: .currentThread(), null);
580: } catch (IllegalAccessException e) {
581: ; // ignore
582: } catch (InvocationTargetException e) {
583: /**
584: * InvocationTargetException is thrown by 'invoke' when
585: * the method being invoked (getContextClassLoader) throws
586: * an exception.
587: *
588: * getContextClassLoader() throws SecurityException when
589: * the context class loader isn't an ancestor of the
590: * calling class's class loader, or if security
591: * permissions are restricted.
592: *
593: * In the first case (not related), we want to ignore and
594: * keep going. We cannot help but also ignore the second
595: * with the logic below, but other calls elsewhere (to
596: * obtain a class loader) will trigger this exception where
597: * we can make a distinction.
598: */
599: if (e.getTargetException() instanceof SecurityException) {
600: ; // ignore
601: } else {
602: // Capture 'e.getTargetException()' exception for details
603: // alternate: log 'e.getTargetException()', and pass back 'e'.
604: throw new LogConfigurationException(
605: "Unexpected InvocationTargetException",
606: e.getTargetException());
607: }
608: }
609: } catch (NoSuchMethodException e) {
610: // Assume we are running on JDK 1.1
611: ; // ignore
612: }
613: }
614:
615: if (classLoader == null) {
616: classLoader = SimpleLog.class.getClassLoader();
617: }
618:
619: // Return the selected class loader
620: return classLoader;
621: }
622:
623: private static InputStream getResourceAsStream(final String name) {
624: return (InputStream) AccessController
625: .doPrivileged(new PrivilegedAction() {
626: public Object run() {
627: ClassLoader threadCL = getContextClassLoader();
628:
629: if (threadCL != null) {
630: return threadCL.getResourceAsStream(name);
631: } else {
632: return ClassLoader
633: .getSystemResourceAsStream(name);
634: }
635: }
636: });
637: }
638: }
|