001/** 002 * Copyright (c) 2004-2012 QOS.ch 003 * All rights reserved. 004 * 005 * Permission is hereby granted, free of charge, to any person obtaining 006 * a copy of this software and associated documentation files (the 007 * "Software"), to deal in the Software without restriction, including 008 * without limitation the rights to use, copy, modify, merge, publish, 009 * distribute, sublicense, and/or sell copies of the Software, and to 010 * permit persons to whom the Software is furnished to do so, subject to 011 * the following conditions: 012 * 013 * The above copyright notice and this permission notice shall be 014 * included in all copies or substantial portions of the Software. 015 * 016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 017 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 018 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 019 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 020 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 021 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 022 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 023 * 024 */ 025package org.slf4j.impl; 026 027import java.io.PrintStream; 028import java.util.Date; 029 030import org.slf4j.Logger; 031import org.slf4j.event.LoggingEvent; 032import org.slf4j.helpers.FormattingTuple; 033import org.slf4j.helpers.MarkerIgnoringBase; 034import org.slf4j.helpers.MessageFormatter; 035import org.slf4j.spi.LocationAwareLogger; 036 037/** 038 * <p> 039 * Simple implementation of {@link Logger} that sends all enabled log messages, 040 * for all defined loggers, to the console ({@code System.err}). The following 041 * system properties are supported to configure the behavior of this logger: 042 * </p> 043 * 044 * <ul> 045 * <li><code>org.slf4j.simpleLogger.logFile</code> - The output target which can 046 * be the <em>path</em> to a file, or the special values "System.out" and 047 * "System.err". Default is "System.err".</li> 048 * 049 * <li><code>org.slf4j.simpleLogger.cacheOutputStream</code> - If the output 050 * target is set to "System.out" or "System.err" (see preceding entry), by 051 * default, logs will be output to the latest value referenced by 052 * <code>System.out/err</code> variables. By setting this 053 * parameter to true, the output stream will be cached, i.e. assigned once at 054 * initialization time and re-used independently of the current value referenced by 055 * <code>System.out/err</code>. 056 * </li> 057 * 058 * <li><code>org.slf4j.simpleLogger.defaultLogLevel</code> - Default log level 059 * for all instances of SimpleLogger. Must be one of ("trace", "debug", "info", 060 * "warn", "error" or "off"). If not specified, defaults to "info".</li> 061 * 062 * <li><code>org.slf4j.simpleLogger.log.<em>a.b.c</em></code> - Logging detail 063 * level for a SimpleLogger instance named "a.b.c". Right-side value must be one 064 * of "trace", "debug", "info", "warn", "error" or "off". When a SimpleLogger 065 * named "a.b.c" is initialized, its level is assigned from this property. If 066 * unspecified, the level of nearest parent logger will be used, and if none is 067 * set, then the value specified by 068 * <code>org.slf4j.simpleLogger.defaultLogLevel</code> will be used.</li> 069 * 070 * <li><code>org.slf4j.simpleLogger.showDateTime</code> - Set to 071 * <code>true</code> if you want the current date and time to be included in 072 * output messages. Default is <code>false</code></li> 073 * 074 * <li><code>org.slf4j.simpleLogger.dateTimeFormat</code> - The date and time 075 * format to be used in the output messages. The pattern describing the date and 076 * time format is defined by <a href= 077 * "http://docs.oracle.com/javase/1.5.0/docs/api/java/text/SimpleDateFormat.html"> 078 * <code>SimpleDateFormat</code></a>. If the format is not specified or is 079 * invalid, the number of milliseconds since start up will be output.</li> 080 * 081 * <li><code>org.slf4j.simpleLogger.showThreadName</code> -Set to 082 * <code>true</code> if you want to output the current thread name. Defaults to 083 * <code>true</code>.</li> 084 * 085 * <li><code>org.slf4j.simpleLogger.showLogName</code> - Set to 086 * <code>true</code> if you want the Logger instance name to be included in 087 * output messages. Defaults to <code>true</code>.</li> 088 * 089 * <li><code>org.slf4j.simpleLogger.showShortLogName</code> - Set to 090 * <code>true</code> if you want the last component of the name to be included 091 * in output messages. Defaults to <code>false</code>.</li> 092 * 093 * <li><code>org.slf4j.simpleLogger.levelInBrackets</code> - Should the level 094 * string be output in brackets? Defaults to <code>false</code>.</li> 095 * 096 * <li><code>org.slf4j.simpleLogger.warnLevelString</code> - The string value 097 * output for the warn level. Defaults to <code>WARN</code>.</li> 098 * 099 * </ul> 100 * 101 * <p> 102 * In addition to looking for system properties with the names specified above, 103 * this implementation also checks for a class loader resource named 104 * <code>"simplelogger.properties"</code>, and includes any matching definitions 105 * from this resource (if it exists). 106 * </p> 107 * 108 * <p> 109 * With no configuration, the default output includes the relative time in 110 * milliseconds, thread name, the level, logger name, and the message followed 111 * by the line separator for the host. In log4j terms it amounts to the "%r [%t] 112 * %level %logger - %m%n" pattern. 113 * </p> 114 * <p> 115 * Sample output follows. 116 * </p> 117 * 118 * <pre> 119 * 176 [main] INFO examples.Sort - Populating an array of 2 elements in reverse order. 120 * 225 [main] INFO examples.SortAlgo - Entered the sort method. 121 * 304 [main] INFO examples.SortAlgo - Dump of integer array: 122 * 317 [main] INFO examples.SortAlgo - Element [0] = 0 123 * 331 [main] INFO examples.SortAlgo - Element [1] = 1 124 * 343 [main] INFO examples.Sort - The next log statement should be an error message. 125 * 346 [main] ERROR examples.SortAlgo - Tried to dump an uninitialized array. 126 * at org.log4j.examples.SortAlgo.dump(SortAlgo.java:58) 127 * at org.log4j.examples.Sort.main(Sort.java:64) 128 * 467 [main] INFO examples.Sort - Exiting main method. 129 * </pre> 130 * 131 * <p> 132 * This implementation is heavily inspired by 133 * <a href="http://commons.apache.org/logging/">Apache Commons Logging</a>'s 134 * SimpleLog. 135 * </p> 136 * 137 * @author Ceki Gülcü 138 * @author Scott Sanders 139 * @author Rod Waldhoff 140 * @author Robert Burrell Donkin 141 * @author Cédrik LIME 142 */ 143public class SimpleLogger extends MarkerIgnoringBase { 144 145 private static final long serialVersionUID = -632788891211436180L; 146 147 private static long START_TIME = System.currentTimeMillis(); 148 149 protected static final int LOG_LEVEL_TRACE = LocationAwareLogger.TRACE_INT; 150 protected static final int LOG_LEVEL_DEBUG = LocationAwareLogger.DEBUG_INT; 151 protected static final int LOG_LEVEL_INFO = LocationAwareLogger.INFO_INT; 152 protected static final int LOG_LEVEL_WARN = LocationAwareLogger.WARN_INT; 153 protected static final int LOG_LEVEL_ERROR = LocationAwareLogger.ERROR_INT; 154 // The OFF level can only be used in configuration files to disable logging. 155 // It has 156 // no printing method associated with it in o.s.Logger interface. 157 protected static final int LOG_LEVEL_OFF = LOG_LEVEL_ERROR + 10; 158 159 private static boolean INITIALIZED = false; 160 static SimpleLoggerConfiguration CONFIG_PARAMS = null; 161 162 static void lazyInit() { 163 if (INITIALIZED) { 164 return; 165 } 166 INITIALIZED = true; 167 init(); 168 } 169 170 // external software might be invoking this method directly. Do not rename 171 // or change its semantics. 172 static void init() { 173 CONFIG_PARAMS = new SimpleLoggerConfiguration(); 174 CONFIG_PARAMS.init(); 175 } 176 177 /** The current log level */ 178 protected int currentLogLevel = LOG_LEVEL_INFO; 179 /** The short name of this simple log instance */ 180 private transient String shortLogName = null; 181 182 /** 183 * All system properties used by <code>SimpleLogger</code> start with this 184 * prefix 185 */ 186 public static final String SYSTEM_PREFIX = "org.slf4j.simpleLogger."; 187 188 public static final String LOG_KEY_PREFIX = SimpleLogger.SYSTEM_PREFIX + "log."; 189 190 public static final String CACHE_OUTPUT_STREAM_STRING_KEY = SimpleLogger.SYSTEM_PREFIX + "cacheOutputStream"; 191 192 public static final String WARN_LEVEL_STRING_KEY = SimpleLogger.SYSTEM_PREFIX + "warnLevelString"; 193 194 public static final String LEVEL_IN_BRACKETS_KEY = SimpleLogger.SYSTEM_PREFIX + "levelInBrackets"; 195 196 public static final String LOG_FILE_KEY = SimpleLogger.SYSTEM_PREFIX + "logFile"; 197 198 public static final String SHOW_SHORT_LOG_NAME_KEY = SimpleLogger.SYSTEM_PREFIX + "showShortLogName"; 199 200 public static final String SHOW_LOG_NAME_KEY = SimpleLogger.SYSTEM_PREFIX + "showLogName"; 201 202 public static final String SHOW_THREAD_NAME_KEY = SimpleLogger.SYSTEM_PREFIX + "showThreadName"; 203 204 public static final String DATE_TIME_FORMAT_KEY = SimpleLogger.SYSTEM_PREFIX + "dateTimeFormat"; 205 206 public static final String SHOW_DATE_TIME_KEY = SimpleLogger.SYSTEM_PREFIX + "showDateTime"; 207 208 public static final String DEFAULT_LOG_LEVEL_KEY = SimpleLogger.SYSTEM_PREFIX + "defaultLogLevel"; 209 210 /** 211 * Package access allows only {@link SimpleLoggerFactory} to instantiate 212 * SimpleLogger instances. 213 */ 214 SimpleLogger(String name) { 215 this.name = name; 216 217 String levelString = recursivelyComputeLevelString(); 218 if (levelString != null) { 219 this.currentLogLevel = SimpleLoggerConfiguration.stringToLevel(levelString); 220 } else { 221 this.currentLogLevel = CONFIG_PARAMS.defaultLogLevel; 222 } 223 } 224 225 String recursivelyComputeLevelString() { 226 String tempName = name; 227 String levelString = null; 228 int indexOfLastDot = tempName.length(); 229 while ((levelString == null) && (indexOfLastDot > -1)) { 230 tempName = tempName.substring(0, indexOfLastDot); 231 levelString = CONFIG_PARAMS.getStringProperty(SimpleLogger.LOG_KEY_PREFIX + tempName, null); 232 indexOfLastDot = String.valueOf(tempName).lastIndexOf("."); 233 } 234 return levelString; 235 } 236 237 /** 238 * This is our internal implementation for logging regular 239 * (non-parameterized) log messages. 240 * 241 * @param level 242 * One of the LOG_LEVEL_XXX constants defining the log level 243 * @param message 244 * The message itself 245 * @param t 246 * The exception whose stack trace should be logged 247 */ 248 private void log(int level, String message, Throwable t) { 249 if (!isLevelEnabled(level)) { 250 return; 251 } 252 253 StringBuilder buf = new StringBuilder(32); 254 255 // Append date-time if so configured 256 if (CONFIG_PARAMS.showDateTime) { 257 if (CONFIG_PARAMS.dateFormatter != null) { 258 buf.append(getFormattedDate()); 259 buf.append(' '); 260 } else { 261 buf.append(System.currentTimeMillis() - START_TIME); 262 buf.append(' '); 263 } 264 } 265 266 // Append current thread name if so configured 267 if (CONFIG_PARAMS.showThreadName) { 268 buf.append('['); 269 buf.append(Thread.currentThread().getName()); 270 buf.append("] "); 271 } 272 273 if (CONFIG_PARAMS.levelInBrackets) 274 buf.append('['); 275 276 // Append a readable representation of the log level 277 String levelStr = renderLevel(level); 278 buf.append(levelStr); 279 if (CONFIG_PARAMS.levelInBrackets) 280 buf.append(']'); 281 buf.append(' '); 282 283 // Append the name of the log instance if so configured 284 if (CONFIG_PARAMS.showShortLogName) { 285 if (shortLogName == null) 286 shortLogName = computeShortName(); 287 buf.append(String.valueOf(shortLogName)).append(" - "); 288 } else if (CONFIG_PARAMS.showLogName) { 289 buf.append(String.valueOf(name)).append(" - "); 290 } 291 292 // Append the message 293 buf.append(message); 294 295 write(buf, t); 296 297 } 298 299 protected String renderLevel(int level) { 300 switch (level) { 301 case LOG_LEVEL_TRACE: 302 return "TRACE"; 303 case LOG_LEVEL_DEBUG: 304 return ("DEBUG"); 305 case LOG_LEVEL_INFO: 306 return "INFO"; 307 case LOG_LEVEL_WARN: 308 return CONFIG_PARAMS.warnLevelString; 309 case LOG_LEVEL_ERROR: 310 return "ERROR"; 311 } 312 throw new IllegalStateException("Unrecognized level [" + level + "]"); 313 } 314 315 void write(StringBuilder buf, Throwable t) { 316 PrintStream targetStream = CONFIG_PARAMS.outputChoice.getTargetPrintStream(); 317 318 targetStream.println(buf.toString()); 319 writeThrowable(t, targetStream); 320 targetStream.flush(); 321 } 322 323 protected void writeThrowable(Throwable t, PrintStream targetStream) { 324 if (t != null) { 325 t.printStackTrace(targetStream); 326 } 327 } 328 329 private String getFormattedDate() { 330 Date now = new Date(); 331 String dateText; 332 synchronized (CONFIG_PARAMS.dateFormatter) { 333 dateText = CONFIG_PARAMS.dateFormatter.format(now); 334 } 335 return dateText; 336 } 337 338 private String computeShortName() { 339 return name.substring(name.lastIndexOf(".") + 1); 340 } 341 342 /** 343 * For formatted messages, first substitute arguments and then log. 344 * 345 * @param level 346 * @param format 347 * @param arg1 348 * @param arg2 349 */ 350 private void formatAndLog(int level, String format, Object arg1, Object arg2) { 351 if (!isLevelEnabled(level)) { 352 return; 353 } 354 FormattingTuple tp = MessageFormatter.format(format, arg1, arg2); 355 log(level, tp.getMessage(), tp.getThrowable()); 356 } 357 358 /** 359 * For formatted messages, first substitute arguments and then log. 360 * 361 * @param level 362 * @param format 363 * @param arguments 364 * a list of 3 ore more arguments 365 */ 366 private void formatAndLog(int level, String format, Object... arguments) { 367 if (!isLevelEnabled(level)) { 368 return; 369 } 370 FormattingTuple tp = MessageFormatter.arrayFormat(format, arguments); 371 log(level, tp.getMessage(), tp.getThrowable()); 372 } 373 374 /** 375 * Is the given log level currently enabled? 376 * 377 * @param logLevel 378 * is this level enabled? 379 */ 380 protected boolean isLevelEnabled(int logLevel) { 381 // log level are numerically ordered so can use simple numeric 382 // comparison 383 return (logLevel >= currentLogLevel); 384 } 385 386 /** Are {@code trace} messages currently enabled? */ 387 public boolean isTraceEnabled() { 388 return isLevelEnabled(LOG_LEVEL_TRACE); 389 } 390 391 /** 392 * A simple implementation which logs messages of level TRACE according to 393 * the format outlined above. 394 */ 395 public void trace(String msg) { 396 log(LOG_LEVEL_TRACE, msg, null); 397 } 398 399 /** 400 * Perform single parameter substitution before logging the message of level 401 * TRACE according to the format outlined above. 402 */ 403 public void trace(String format, Object param1) { 404 formatAndLog(LOG_LEVEL_TRACE, format, param1, null); 405 } 406 407 /** 408 * Perform double parameter substitution before logging the message of level 409 * TRACE according to the format outlined above. 410 */ 411 public void trace(String format, Object param1, Object param2) { 412 formatAndLog(LOG_LEVEL_TRACE, format, param1, param2); 413 } 414 415 /** 416 * Perform double parameter substitution before logging the message of level 417 * TRACE according to the format outlined above. 418 */ 419 public void trace(String format, Object... argArray) { 420 formatAndLog(LOG_LEVEL_TRACE, format, argArray); 421 } 422 423 /** Log a message of level TRACE, including an exception. */ 424 public void trace(String msg, Throwable t) { 425 log(LOG_LEVEL_TRACE, msg, t); 426 } 427 428 /** Are {@code debug} messages currently enabled? */ 429 public boolean isDebugEnabled() { 430 return isLevelEnabled(LOG_LEVEL_DEBUG); 431 } 432 433 /** 434 * A simple implementation which logs messages of level DEBUG according to 435 * the format outlined above. 436 */ 437 public void debug(String msg) { 438 log(LOG_LEVEL_DEBUG, msg, null); 439 } 440 441 /** 442 * Perform single parameter substitution before logging the message of level 443 * DEBUG according to the format outlined above. 444 */ 445 public void debug(String format, Object param1) { 446 formatAndLog(LOG_LEVEL_DEBUG, format, param1, null); 447 } 448 449 /** 450 * Perform double parameter substitution before logging the message of level 451 * DEBUG according to the format outlined above. 452 */ 453 public void debug(String format, Object param1, Object param2) { 454 formatAndLog(LOG_LEVEL_DEBUG, format, param1, param2); 455 } 456 457 /** 458 * Perform double parameter substitution before logging the message of level 459 * DEBUG according to the format outlined above. 460 */ 461 public void debug(String format, Object... argArray) { 462 formatAndLog(LOG_LEVEL_DEBUG, format, argArray); 463 } 464 465 /** Log a message of level DEBUG, including an exception. */ 466 public void debug(String msg, Throwable t) { 467 log(LOG_LEVEL_DEBUG, msg, t); 468 } 469 470 /** Are {@code info} messages currently enabled? */ 471 public boolean isInfoEnabled() { 472 return isLevelEnabled(LOG_LEVEL_INFO); 473 } 474 475 /** 476 * A simple implementation which logs messages of level INFO according to 477 * the format outlined above. 478 */ 479 public void info(String msg) { 480 log(LOG_LEVEL_INFO, msg, null); 481 } 482 483 /** 484 * Perform single parameter substitution before logging the message of level 485 * INFO according to the format outlined above. 486 */ 487 public void info(String format, Object arg) { 488 formatAndLog(LOG_LEVEL_INFO, format, arg, null); 489 } 490 491 /** 492 * Perform double parameter substitution before logging the message of level 493 * INFO according to the format outlined above. 494 */ 495 public void info(String format, Object arg1, Object arg2) { 496 formatAndLog(LOG_LEVEL_INFO, format, arg1, arg2); 497 } 498 499 /** 500 * Perform double parameter substitution before logging the message of level 501 * INFO according to the format outlined above. 502 */ 503 public void info(String format, Object... argArray) { 504 formatAndLog(LOG_LEVEL_INFO, format, argArray); 505 } 506 507 /** Log a message of level INFO, including an exception. */ 508 public void info(String msg, Throwable t) { 509 log(LOG_LEVEL_INFO, msg, t); 510 } 511 512 /** Are {@code warn} messages currently enabled? */ 513 public boolean isWarnEnabled() { 514 return isLevelEnabled(LOG_LEVEL_WARN); 515 } 516 517 /** 518 * A simple implementation which always logs messages of level WARN 519 * according to the format outlined above. 520 */ 521 public void warn(String msg) { 522 log(LOG_LEVEL_WARN, msg, null); 523 } 524 525 /** 526 * Perform single parameter substitution before logging the message of level 527 * WARN according to the format outlined above. 528 */ 529 public void warn(String format, Object arg) { 530 formatAndLog(LOG_LEVEL_WARN, format, arg, null); 531 } 532 533 /** 534 * Perform double parameter substitution before logging the message of level 535 * WARN according to the format outlined above. 536 */ 537 public void warn(String format, Object arg1, Object arg2) { 538 formatAndLog(LOG_LEVEL_WARN, format, arg1, arg2); 539 } 540 541 /** 542 * Perform double parameter substitution before logging the message of level 543 * WARN according to the format outlined above. 544 */ 545 public void warn(String format, Object... argArray) { 546 formatAndLog(LOG_LEVEL_WARN, format, argArray); 547 } 548 549 /** Log a message of level WARN, including an exception. */ 550 public void warn(String msg, Throwable t) { 551 log(LOG_LEVEL_WARN, msg, t); 552 } 553 554 /** Are {@code error} messages currently enabled? */ 555 public boolean isErrorEnabled() { 556 return isLevelEnabled(LOG_LEVEL_ERROR); 557 } 558 559 /** 560 * A simple implementation which always logs messages of level ERROR 561 * according to the format outlined above. 562 */ 563 public void error(String msg) { 564 log(LOG_LEVEL_ERROR, msg, null); 565 } 566 567 /** 568 * Perform single parameter substitution before logging the message of level 569 * ERROR according to the format outlined above. 570 */ 571 public void error(String format, Object arg) { 572 formatAndLog(LOG_LEVEL_ERROR, format, arg, null); 573 } 574 575 /** 576 * Perform double parameter substitution before logging the message of level 577 * ERROR according to the format outlined above. 578 */ 579 public void error(String format, Object arg1, Object arg2) { 580 formatAndLog(LOG_LEVEL_ERROR, format, arg1, arg2); 581 } 582 583 /** 584 * Perform double parameter substitution before logging the message of level 585 * ERROR according to the format outlined above. 586 */ 587 public void error(String format, Object... argArray) { 588 formatAndLog(LOG_LEVEL_ERROR, format, argArray); 589 } 590 591 /** Log a message of level ERROR, including an exception. */ 592 public void error(String msg, Throwable t) { 593 log(LOG_LEVEL_ERROR, msg, t); 594 } 595 596 public void log(LoggingEvent event) { 597 int levelInt = event.getLevel().toInt(); 598 599 if (!isLevelEnabled(levelInt)) { 600 return; 601 } 602 FormattingTuple tp = MessageFormatter.arrayFormat(event.getMessage(), event.getArgumentArray(), event.getThrowable()); 603 log(levelInt, tp.getMessage(), event.getThrowable()); 604 } 605 606}