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 018package org.apache.commons.logging.impl; 019 020import java.io.InputStream; 021import java.io.Serializable; 022import java.lang.reflect.InvocationTargetException; 023import java.lang.reflect.Method; 024import java.security.AccessController; 025import java.security.PrivilegedAction; 026import java.text.DateFormat; 027import java.text.SimpleDateFormat; 028import java.util.Date; 029import java.util.Properties; 030 031import org.apache.commons.logging.Log; 032import 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 915605 2014-07-09 20:22:43Z tn $ 070 */ 071public 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