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