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