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