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