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 17 18 package org.apache.commons.logging.impl; 19 20 import java.io.InputStream; 21 import java.io.Serializable; 22 import java.lang.reflect.InvocationTargetException; 23 import java.lang.reflect.Method; 24 import java.security.AccessController; 25 import java.security.PrivilegedAction; 26 import java.text.DateFormat; 27 import java.text.SimpleDateFormat; 28 import java.util.Date; 29 import java.util.Properties; 30 31 import org.apache.commons.logging.Log; 32 import org.apache.commons.logging.LogConfigurationException; 33 34 /** 35 * <p>Simple implementation of Log that sends all enabled log messages, 36 * for all defined loggers, to System.err. The following system properties 37 * are supported to configure the behavior of this logger:</p> 38 * <ul> 39 * <li><code>org.apache.commons.logging.simplelog.defaultlog</code> - 40 * Default logging detail level for all instances of SimpleLog. 41 * Must be one of ("trace", "debug", "info", "warn", "error", or "fatal"). 42 * If not specified, defaults to "info". </li> 43 * <li><code>org.apache.commons.logging.simplelog.log.xxxxx</code> - 44 * Logging detail level for a SimpleLog instance named "xxxxx". 45 * Must be one of ("trace", "debug", "info", "warn", "error", or "fatal"). 46 * If not specified, the default logging detail level is used.</li> 47 * <li><code>org.apache.commons.logging.simplelog.showlogname</code> - 48 * Set to <code>true</code> if you want the Log instance name to be 49 * included in output messages. Defaults to <code>false</code>.</li> 50 * <li><code>org.apache.commons.logging.simplelog.showShortLogname</code> - 51 * Set to <code>true</code> if you want the last component of the name to be 52 * included in output messages. Defaults to <code>true</code>.</li> 53 * <li><code>org.apache.commons.logging.simplelog.showdatetime</code> - 54 * Set to <code>true</code> if you want the current date and time 55 * to be included in output messages. Default is <code>false</code>.</li> 56 * <li><code>org.apache.commons.logging.simplelog.dateTimeFormat</code> - 57 * The date and time format to be used in the output messages. 58 * The pattern describing the date and time format is the same that is 59 * used in <code>java.text.SimpleDateFormat</code>. If the format is not 60 * specified or is invalid, the default format is used. 61 * The default format is <code>yyyy/MM/dd HH:mm:ss:SSS zzz</code>.</li> 62 * </ul> 63 * 64 * <p>In addition to looking for system properties with the names specified 65 * above, this implementation also checks for a class loader resource named 66 * <code>"simplelog.properties"</code>, and includes any matching definitions 67 * from this resource (if it exists).</p> 68 * 69 * @author <a href="mailto:sanders (at) apache.org">Scott Sanders</a> 70 * @author Rod Waldhoff 71 * @author Robert Burrell Donkin 72 * 73 * @version $Id: SimpleLog.java 399221 2006-05-03 09:20:24Z dennisl $ 74 */ 75 public class SimpleLog implements Log, Serializable { 76 77 78 // ------------------------------------------------------- Class Attributes 79 80 /** All system properties used by <code>SimpleLog</code> start with this */ 81 static protected final String systemPrefix = 82 "org.apache.commons.logging.simplelog."; 83 84 /** Properties loaded from simplelog.properties */ 85 static protected final Properties simpleLogProps = new Properties(); 86 87 /** The default format to use when formating dates */ 88 static protected final String DEFAULT_DATE_TIME_FORMAT = 89 "yyyy/MM/dd HH:mm:ss:SSS zzz"; 90 91 /** Include the instance name in the log message? */ 92 static protected boolean showLogName = false; 93 /** Include the short name ( last component ) of the logger in the log 94 * message. Defaults to true - otherwise we'll be lost in a flood of 95 * messages without knowing who sends them. 96 */ 97 static protected boolean showShortName = true; 98 /** Include the current time in the log message */ 99 static protected boolean showDateTime = false; 100 /** The date and time format to use in the log message */ 101 static protected String dateTimeFormat = DEFAULT_DATE_TIME_FORMAT; 102 /** Used to format times */ 103 static protected DateFormat dateFormatter = null; 104 105 // ---------------------------------------------------- Log Level Constants 106 107 108 /** "Trace" level logging. */ 109 public static final int LOG_LEVEL_TRACE = 1; 110 /** "Debug" level logging. */ 111 public static final int LOG_LEVEL_DEBUG = 2; 112 /** "Info" level logging. */ 113 public static final int LOG_LEVEL_INFO = 3; 114 /** "Warn" level logging. */ 115 public static final int LOG_LEVEL_WARN = 4; 116 /** "Error" level logging. */ 117 public static final int LOG_LEVEL_ERROR = 5; 118 /** "Fatal" level logging. */ 119 public static final int LOG_LEVEL_FATAL = 6; 120 121 /** Enable all logging levels */ 122 public static final int LOG_LEVEL_ALL = (LOG_LEVEL_TRACE - 1); 123 124 /** Enable no logging levels */ 125 public static final int LOG_LEVEL_OFF = (LOG_LEVEL_FATAL + 1); 126 127 // ------------------------------------------------------------ Initializer 128 129 private static String getStringProperty(String name) { 130 String prop = null; 131 try { 132 prop = System.getProperty(name); 133 } catch (SecurityException e) { 134 ; // Ignore 135 } 136 return (prop == null) ? simpleLogProps.getProperty(name) : prop; 137 } 138 139 private static String getStringProperty(String name, String dephault) { 140 String prop = getStringProperty(name); 141 return (prop == null) ? dephault : prop; 142 } 143 144 private static boolean getBooleanProperty(String name, boolean dephault) { 145 String prop = getStringProperty(name); 146 return (prop == null) ? dephault : "true".equalsIgnoreCase(prop); 147 } 148 149 // Initialize class attributes. 150 // Load properties file, if found. 151 // Override with system properties. 152 static { 153 // Add props from the resource simplelog.properties 154 InputStream in = getResourceAsStream("simplelog.properties"); 155 if(null != in) { 156 try { 157 simpleLogProps.load(in); 158 in.close(); 159 } catch(java.io.IOException e) { 160 // ignored 161 } 162 } 163 164 showLogName = getBooleanProperty( systemPrefix + "showlogname", showLogName); 165 showShortName = getBooleanProperty( systemPrefix + "showShortLogname", showShortName); 166 showDateTime = getBooleanProperty( systemPrefix + "showdatetime", showDateTime); 167 168 if(showDateTime) { 169 dateTimeFormat = getStringProperty(systemPrefix + "dateTimeFormat", 170 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 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 192 // ------------------------------------------------------------ Constructor 193 194 /** 195 * Construct a simple log with given name. 196 * 197 * @param name 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 242 // -------------------------------------------------------- Properties 243 244 /** 245 * <p> Set logging level. </p> 246 * 247 * @param currentLogLevel new logging level 248 */ 249 public void setLevel(int currentLogLevel) { 250 251 this.currentLogLevel = currentLogLevel; 252 253 } 254 255 256 /** 257 * <p> Get logging level. </p> 258 */ 259 public int getLevel() { 260 261 return currentLogLevel; 262 } 263 264 265 // -------------------------------------------------------- Logging Methods 266 267 268 /** 269 * <p> Do the actual logging. 270 * This method assembles the message 271 * and then calls <code>write()</code> to cause it to be written.</p> 272 * 273 * @param type One of the LOG_LEVEL_XXX constants defining the log level 274 * @param message The message itself (typically a String) 275 * @param t The exception whose stack trace should be logged 276 */ 277 protected void log(int type, Object message, Throwable t) { 278 // Use a string buffer for better performance 279 StringBuffer buf = new StringBuffer(); 280 281 // Append date-time if so configured 282 if(showDateTime) { 283 buf.append(dateFormatter.format(new Date())); 284 buf.append(" "); 285 } 286 287 // Append a readable representation of the log level 288 switch(type) { 289 case SimpleLog.LOG_LEVEL_TRACE: buf.append("[TRACE] "); break; 290 case SimpleLog.LOG_LEVEL_DEBUG: buf.append("[DEBUG] "); break; 291 case SimpleLog.LOG_LEVEL_INFO: buf.append("[INFO] "); break; 292 case SimpleLog.LOG_LEVEL_WARN: buf.append("[WARN] "); break; 293 case SimpleLog.LOG_LEVEL_ERROR: buf.append("[ERROR] "); break; 294 case SimpleLog.LOG_LEVEL_FATAL: buf.append("[FATAL] "); break; 295 } 296 297 // Append the name of the log instance if so configured 298 if( showShortName) { 299 if( shortLogName==null ) { 300 // Cut all but the last component of the name for both styles 301 shortLogName = logName.substring(logName.lastIndexOf(".") + 1); 302 shortLogName = 303 shortLogName.substring(shortLogName.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 java.io.StringWriter sw= new java.io.StringWriter(1024); 320 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 332 /** 333 * <p>Write the content of the message accumulated in the specified 334 * <code>StringBuffer</code> to the appropriate output destination. The 335 * default implementation writes to <code>System.err</code>.</p> 336 * 337 * @param buffer A <code>StringBuffer</code> containing the accumulated 338 * text to be logged 339 */ 340 protected void write(StringBuffer buffer) { 341 342 System.err.println(buffer.toString()); 343 344 } 345 346 347 /** 348 * Is the given log level currently enabled? 349 * 350 * @param logLevel is this level enabled? 351 */ 352 protected boolean isLevelEnabled(int logLevel) { 353 // log level are numerically ordered so can use simple numeric 354 // comparison 355 return (logLevel >= currentLogLevel); 356 } 357 358 359 // -------------------------------------------------------- Log Implementation 360 361 362 /** 363 * Logs a message with 364 * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_DEBUG</code>. 365 * 366 * @param message to log 367 * @see org.apache.commons.logging.Log#debug(Object) 368 */ 369 public final void debug(Object message) { 370 371 if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) { 372 log(SimpleLog.LOG_LEVEL_DEBUG, message, null); 373 } 374 } 375 376 377 /** 378 * Logs a message with 379 * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_DEBUG</code>. 380 * 381 * @param message to log 382 * @param t log this cause 383 * @see org.apache.commons.logging.Log#debug(Object, Throwable) 384 */ 385 public final void debug(Object message, Throwable t) { 386 387 if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) { 388 log(SimpleLog.LOG_LEVEL_DEBUG, message, t); 389 } 390 } 391 392 393 /** 394 * Logs a message with 395 * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_TRACE</code>. 396 * 397 * @param message to log 398 * @see org.apache.commons.logging.Log#trace(Object) 399 */ 400 public final void trace(Object message) { 401 402 if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) { 403 log(SimpleLog.LOG_LEVEL_TRACE, message, null); 404 } 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 /** 425 * Logs a message with 426 * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_INFO</code>. 427 * 428 * @param message to log 429 * @see org.apache.commons.logging.Log#info(Object) 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 /** 440 * Logs a message with 441 * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_INFO</code>. 442 * 443 * @param message to log 444 * @param t log this cause 445 * @see org.apache.commons.logging.Log#info(Object, Throwable) 446 */ 447 public final void info(Object message, Throwable t) { 448 449 if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) { 450 log(SimpleLog.LOG_LEVEL_INFO, message, t); 451 } 452 } 453 454 455 /** 456 * Logs a message with 457 * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_WARN</code>. 458 * 459 * @param message to log 460 * @see org.apache.commons.logging.Log#warn(Object) 461 */ 462 public final void warn(Object message) { 463 464 if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) { 465 log(SimpleLog.LOG_LEVEL_WARN, message, null); 466 } 467 } 468 469 470 /** 471 * Logs a message with 472 * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_WARN</code>. 473 * 474 * @param message to log 475 * @param t log this cause 476 * @see org.apache.commons.logging.Log#warn(Object, Throwable) 477 */ 478 public final void warn(Object message, Throwable t) { 479 480 if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) { 481 log(SimpleLog.LOG_LEVEL_WARN, message, t); 482 } 483 } 484 485 486 /** 487 * Logs a message with 488 * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_ERROR</code>. 489 * 490 * @param message to log 491 * @see org.apache.commons.logging.Log#error(Object) 492 */ 493 public final void error(Object message) { 494 495 if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) { 496 log(SimpleLog.LOG_LEVEL_ERROR, message, null); 497 } 498 } 499 500 501 /** 502 * Logs a message with 503 * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_ERROR</code>. 504 * 505 * @param message to log 506 * @param t log this cause 507 * @see org.apache.commons.logging.Log#error(Object, Throwable) 508 */ 509 public final void error(Object message, Throwable t) { 510 511 if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) { 512 log(SimpleLog.LOG_LEVEL_ERROR, message, t); 513 } 514 } 515 516 517 /** 518 * Log a message with 519 * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_FATAL</code>. 520 * 521 * @param message to log 522 * @see org.apache.commons.logging.Log#fatal(Object) 523 */ 524 public final void fatal(Object message) { 525 526 if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) { 527 log(SimpleLog.LOG_LEVEL_FATAL, message, null); 528 } 529 } 530 531 532 /** 533 * Logs a message with 534 * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_FATAL</code>. 535 * 536 * @param message to log 537 * @param t log this cause 538 * @see org.apache.commons.logging.Log#fatal(Object, Throwable) 539 */ 540 public final void fatal(Object message, Throwable t) { 541 542 if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) { 543 log(SimpleLog.LOG_LEVEL_FATAL, message, t); 544 } 545 } 546 547 548 /** 549 * <p> Are debug messages currently enabled? </p> 550 * 551 * <p> This allows expensive operations such as <code>String</code> 552 * concatenation to be avoided when the message will be ignored by the 553 * logger. </p> 554 */ 555 public final boolean isDebugEnabled() { 556 557 return isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG); 558 } 559 560 561 /** 562 * <p> Are error messages currently enabled? </p> 563 * 564 * <p> This allows expensive operations such as <code>String</code> 565 * concatenation to be avoided when the message will be ignored by the 566 * logger. </p> 567 */ 568 public final boolean isErrorEnabled() { 569 570 return isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR); 571 } 572 573 574 /** 575 * <p> Are fatal messages currently enabled? </p> 576 * 577 * <p> This allows expensive operations such as <code>String</code> 578 * concatenation to be avoided when the message will be ignored by the 579 * logger. </p> 580 */ 581 public final boolean isFatalEnabled() { 582 583 return isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL); 584 } 585 586 587 /** 588 * <p> Are info 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 isInfoEnabled() { 595 596 return isLevelEnabled(SimpleLog.LOG_LEVEL_INFO); 597 } 598 599 600 /** 601 * <p> Are trace messages currently enabled? </p> 602 * 603 * <p> This allows expensive operations such as <code>String</code> 604 * concatenation to be avoided when the message will be ignored by the 605 * logger. </p> 606 */ 607 public final boolean isTraceEnabled() { 608 609 return isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE); 610 } 611 612 613 /** 614 * <p> Are warn messages currently enabled? </p> 615 * 616 * <p> This allows expensive operations such as <code>String</code> 617 * concatenation to be avoided when the message will be ignored by the 618 * logger. </p> 619 */ 620 public final boolean isWarnEnabled() { 621 622 return isLevelEnabled(SimpleLog.LOG_LEVEL_WARN); 623 } 624 625 626 /** 627 * Return the thread context class loader if available. 628 * Otherwise return null. 629 * 630 * The thread context class loader is available for JDK 1.2 631 * or later, if certain security conditions are met. 632 * 633 * @exception LogConfigurationException if a suitable class loader 634 * cannot be identified. 635 */ 636 private static ClassLoader getContextClassLoader() 637 { 638 ClassLoader classLoader = null; 639 640 if (classLoader == null) { 641 try { 642 // Are we running on a JDK 1.2 or later system? 643 Method method = Thread.class.getMethod("getContextClassLoader", 644 (Class[]) null); 645 646 // Get the thread context class loader (if there is one) 647 try { 648 classLoader = (ClassLoader)method.invoke(Thread.currentThread(), 649 (Object[]) null); 650 651 } catch (IllegalAccessException e) { 652 ; // ignore 653 } catch (InvocationTargetException e) { 654 /** 655 * InvocationTargetException is thrown by 'invoke' when 656 * the method being invoked (getContextClassLoader) throws 657 * an exception. 658 * 659 * getContextClassLoader() throws SecurityException when 660 * the context class loader isn't an ancestor of the 661 * calling class's class loader, or if security 662 * permissions are restricted. 663 * 664 * In the first case (not related), we want to ignore and 665 * keep going. We cannot help but also ignore the second 666 * with the logic below, but other calls elsewhere (to 667 * obtain a class loader) will trigger this exception where 668 * we can make a distinction. 669 */ 670 if (e.getTargetException() instanceof SecurityException) { 671 ; // ignore 672 } else { 673 // Capture 'e.getTargetException()' exception for details 674 // alternate: log 'e.getTargetException()', and pass back 'e'. 675 throw new LogConfigurationException 676 ("Unexpected InvocationTargetException", e.getTargetException()); 677 } 678 } 679 } catch (NoSuchMethodException e) { 680 // Assume we are running on JDK 1.1 681 ; // ignore 682 } 683 } 684 685 if (classLoader == null) { 686 classLoader = SimpleLog.class.getClassLoader(); 687 } 688 689 // Return the selected class loader 690 return classLoader; 691 } 692 693 private static InputStream getResourceAsStream(final String name) 694 { 695 return (InputStream)AccessController.doPrivileged( 696 new PrivilegedAction() { 697 public Object run() { 698 ClassLoader threadCL = getContextClassLoader(); 699 700 if (threadCL != null) { 701 return threadCL.getResourceAsStream(name); 702 } else { 703 return ClassLoader.getSystemResourceAsStream(name); 704 } 705 } 706 }); 707 } 708 } 709 710