001/** 002 * Copyright (c) 2004-2011 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.util.logging.Level; 028import java.util.logging.LogRecord; 029 030import org.slf4j.Logger; 031import org.slf4j.Marker; 032import org.slf4j.event.EventConstants; 033import org.slf4j.event.LoggingEvent; 034import org.slf4j.helpers.FormattingTuple; 035import org.slf4j.helpers.MarkerIgnoringBase; 036import org.slf4j.helpers.MessageFormatter; 037import org.slf4j.spi.LocationAwareLogger; 038 039/** 040 * A wrapper over {@link java.util.logging.Logger java.util.logging.Logger} in 041 * conformity with the {@link Logger} interface. Note that the logging levels 042 * mentioned in this class refer to those defined in the java.util.logging 043 * package. 044 * 045 * @author Ceki Gülcü 046 * @author Peter Royal 047 */ 048public final class JDK14LoggerAdapter extends MarkerIgnoringBase implements LocationAwareLogger { 049 050 private static final long serialVersionUID = -8053026990503422791L; 051 052 transient final java.util.logging.Logger logger; 053 054 // WARN: JDK14LoggerAdapter constructor should have only package access so 055 // that only JDK14LoggerFactory be able to create one. 056 JDK14LoggerAdapter(java.util.logging.Logger logger) { 057 this.logger = logger; 058 this.name = logger.getName(); 059 } 060 061 /** 062 * Is this logger instance enabled for the FINEST level? 063 * 064 * @return True if this Logger is enabled for level FINEST, false otherwise. 065 */ 066 public boolean isTraceEnabled() { 067 return logger.isLoggable(Level.FINEST); 068 } 069 070 /** 071 * Log a message object at level FINEST. 072 * 073 * @param msg 074 * - the message object to be logged 075 */ 076 public void trace(String msg) { 077 if (logger.isLoggable(Level.FINEST)) { 078 log(SELF, Level.FINEST, msg, null); 079 } 080 } 081 082 /** 083 * Log a message at level FINEST according to the specified format and 084 * argument. 085 * 086 * <p> 087 * This form avoids superfluous object creation when the logger is disabled 088 * for level FINEST. 089 * </p> 090 * 091 * @param format 092 * the format string 093 * @param arg 094 * the argument 095 */ 096 public void trace(String format, Object arg) { 097 if (logger.isLoggable(Level.FINEST)) { 098 FormattingTuple ft = MessageFormatter.format(format, arg); 099 log(SELF, Level.FINEST, ft.getMessage(), ft.getThrowable()); 100 } 101 } 102 103 /** 104 * Log a message at level FINEST according to the specified format and 105 * arguments. 106 * 107 * <p> 108 * This form avoids superfluous object creation when the logger is disabled 109 * for the FINEST level. 110 * </p> 111 * 112 * @param format 113 * the format string 114 * @param arg1 115 * the first argument 116 * @param arg2 117 * the second argument 118 */ 119 public void trace(String format, Object arg1, Object arg2) { 120 if (logger.isLoggable(Level.FINEST)) { 121 FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); 122 log(SELF, Level.FINEST, ft.getMessage(), ft.getThrowable()); 123 } 124 } 125 126 /** 127 * Log a message at level FINEST according to the specified format and 128 * arguments. 129 * 130 * <p> 131 * This form avoids superfluous object creation when the logger is disabled 132 * for the FINEST level. 133 * </p> 134 * 135 * @param format 136 * the format string 137 * @param argArray 138 * an array of arguments 139 */ 140 public void trace(String format, Object... argArray) { 141 if (logger.isLoggable(Level.FINEST)) { 142 FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray); 143 log(SELF, Level.FINEST, ft.getMessage(), ft.getThrowable()); 144 } 145 } 146 147 /** 148 * Log an exception (throwable) at level FINEST with an accompanying message. 149 * 150 * @param msg 151 * the message accompanying the exception 152 * @param t 153 * the exception (throwable) to log 154 */ 155 public void trace(String msg, Throwable t) { 156 if (logger.isLoggable(Level.FINEST)) { 157 log(SELF, Level.FINEST, msg, t); 158 } 159 } 160 161 /** 162 * Is this logger instance enabled for the FINE level? 163 * 164 * @return True if this Logger is enabled for level FINE, false otherwise. 165 */ 166 public boolean isDebugEnabled() { 167 return logger.isLoggable(Level.FINE); 168 } 169 170 /** 171 * Log a message object at level FINE. 172 * 173 * @param msg 174 * - the message object to be logged 175 */ 176 public void debug(String msg) { 177 if (logger.isLoggable(Level.FINE)) { 178 log(SELF, Level.FINE, msg, null); 179 } 180 } 181 182 /** 183 * Log a message at level FINE according to the specified format and argument. 184 * 185 * <p> 186 * This form avoids superfluous object creation when the logger is disabled 187 * for level FINE. 188 * </p> 189 * 190 * @param format 191 * the format string 192 * @param arg 193 * the argument 194 */ 195 public void debug(String format, Object arg) { 196 if (logger.isLoggable(Level.FINE)) { 197 FormattingTuple ft = MessageFormatter.format(format, arg); 198 log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable()); 199 } 200 } 201 202 /** 203 * Log a message at level FINE according to the specified format and 204 * arguments. 205 * 206 * <p> 207 * This form avoids superfluous object creation when the logger is disabled 208 * for the FINE level. 209 * </p> 210 * 211 * @param format 212 * the format string 213 * @param arg1 214 * the first argument 215 * @param arg2 216 * the second argument 217 */ 218 public void debug(String format, Object arg1, Object arg2) { 219 if (logger.isLoggable(Level.FINE)) { 220 FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); 221 log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable()); 222 } 223 } 224 225 /** 226 * Log a message at level FINE according to the specified format and 227 * arguments. 228 * 229 * <p> 230 * This form avoids superfluous object creation when the logger is disabled 231 * for the FINE level. 232 * </p> 233 * 234 * @param format 235 * the format string 236 * @param argArray 237 * an array of arguments 238 */ 239 public void debug(String format, Object... argArray) { 240 if (logger.isLoggable(Level.FINE)) { 241 FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray); 242 log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable()); 243 } 244 } 245 246 /** 247 * Log an exception (throwable) at level FINE with an accompanying message. 248 * 249 * @param msg 250 * the message accompanying the exception 251 * @param t 252 * the exception (throwable) to log 253 */ 254 public void debug(String msg, Throwable t) { 255 if (logger.isLoggable(Level.FINE)) { 256 log(SELF, Level.FINE, msg, t); 257 } 258 } 259 260 /** 261 * Is this logger instance enabled for the INFO level? 262 * 263 * @return True if this Logger is enabled for the INFO level, false otherwise. 264 */ 265 public boolean isInfoEnabled() { 266 return logger.isLoggable(Level.INFO); 267 } 268 269 /** 270 * Log a message object at the INFO level. 271 * 272 * @param msg 273 * - the message object to be logged 274 */ 275 public void info(String msg) { 276 if (logger.isLoggable(Level.INFO)) { 277 log(SELF, Level.INFO, msg, null); 278 } 279 } 280 281 /** 282 * Log a message at level INFO according to the specified format and argument. 283 * 284 * <p> 285 * This form avoids superfluous object creation when the logger is disabled 286 * for the INFO level. 287 * </p> 288 * 289 * @param format 290 * the format string 291 * @param arg 292 * the argument 293 */ 294 public void info(String format, Object arg) { 295 if (logger.isLoggable(Level.INFO)) { 296 FormattingTuple ft = MessageFormatter.format(format, arg); 297 log(SELF, Level.INFO, ft.getMessage(), ft.getThrowable()); 298 } 299 } 300 301 /** 302 * Log a message at the INFO level according to the specified format and 303 * arguments. 304 * 305 * <p> 306 * This form avoids superfluous object creation when the logger is disabled 307 * for the INFO level. 308 * </p> 309 * 310 * @param format 311 * the format string 312 * @param arg1 313 * the first argument 314 * @param arg2 315 * the second argument 316 */ 317 public void info(String format, Object arg1, Object arg2) { 318 if (logger.isLoggable(Level.INFO)) { 319 FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); 320 log(SELF, Level.INFO, ft.getMessage(), ft.getThrowable()); 321 } 322 } 323 324 /** 325 * Log a message at level INFO according to the specified format and 326 * arguments. 327 * 328 * <p> 329 * This form avoids superfluous object creation when the logger is disabled 330 * for the INFO level. 331 * </p> 332 * 333 * @param format 334 * the format string 335 * @param argArray 336 * an array of arguments 337 */ 338 public void info(String format, Object... argArray) { 339 if (logger.isLoggable(Level.INFO)) { 340 FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray); 341 log(SELF, Level.INFO, ft.getMessage(), ft.getThrowable()); 342 } 343 } 344 345 /** 346 * Log an exception (throwable) at the INFO level with an accompanying 347 * message. 348 * 349 * @param msg 350 * the message accompanying the exception 351 * @param t 352 * the exception (throwable) to log 353 */ 354 public void info(String msg, Throwable t) { 355 if (logger.isLoggable(Level.INFO)) { 356 log(SELF, Level.INFO, msg, t); 357 } 358 } 359 360 /** 361 * Is this logger instance enabled for the WARNING level? 362 * 363 * @return True if this Logger is enabled for the WARNING level, false 364 * otherwise. 365 */ 366 public boolean isWarnEnabled() { 367 return logger.isLoggable(Level.WARNING); 368 } 369 370 /** 371 * Log a message object at the WARNING level. 372 * 373 * @param msg 374 * - the message object to be logged 375 */ 376 public void warn(String msg) { 377 if (logger.isLoggable(Level.WARNING)) { 378 log(SELF, Level.WARNING, msg, null); 379 } 380 } 381 382 /** 383 * Log a message at the WARNING level according to the specified format and 384 * argument. 385 * 386 * <p> 387 * This form avoids superfluous object creation when the logger is disabled 388 * for the WARNING level. 389 * </p> 390 * 391 * @param format 392 * the format string 393 * @param arg 394 * the argument 395 */ 396 public void warn(String format, Object arg) { 397 if (logger.isLoggable(Level.WARNING)) { 398 FormattingTuple ft = MessageFormatter.format(format, arg); 399 log(SELF, Level.WARNING, ft.getMessage(), ft.getThrowable()); 400 } 401 } 402 403 /** 404 * Log a message at the WARNING level according to the specified format and 405 * arguments. 406 * 407 * <p> 408 * This form avoids superfluous object creation when the logger is disabled 409 * for the WARNING level. 410 * </p> 411 * 412 * @param format 413 * the format string 414 * @param arg1 415 * the first argument 416 * @param arg2 417 * the second argument 418 */ 419 public void warn(String format, Object arg1, Object arg2) { 420 if (logger.isLoggable(Level.WARNING)) { 421 FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); 422 log(SELF, Level.WARNING, ft.getMessage(), ft.getThrowable()); 423 } 424 } 425 426 /** 427 * Log a message at level WARNING according to the specified format and 428 * arguments. 429 * 430 * <p> 431 * This form avoids superfluous object creation when the logger is disabled 432 * for the WARNING level. 433 * </p> 434 * 435 * @param format 436 * the format string 437 * @param argArray 438 * an array of arguments 439 */ 440 public void warn(String format, Object... argArray) { 441 if (logger.isLoggable(Level.WARNING)) { 442 FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray); 443 log(SELF, Level.WARNING, ft.getMessage(), ft.getThrowable()); 444 } 445 } 446 447 /** 448 * Log an exception (throwable) at the WARNING level with an accompanying 449 * message. 450 * 451 * @param msg 452 * the message accompanying the exception 453 * @param t 454 * the exception (throwable) to log 455 */ 456 public void warn(String msg, Throwable t) { 457 if (logger.isLoggable(Level.WARNING)) { 458 log(SELF, Level.WARNING, msg, t); 459 } 460 } 461 462 /** 463 * Is this logger instance enabled for level SEVERE? 464 * 465 * @return True if this Logger is enabled for level SEVERE, false otherwise. 466 */ 467 public boolean isErrorEnabled() { 468 return logger.isLoggable(Level.SEVERE); 469 } 470 471 /** 472 * Log a message object at the SEVERE level. 473 * 474 * @param msg 475 * - the message object to be logged 476 */ 477 public void error(String msg) { 478 if (logger.isLoggable(Level.SEVERE)) { 479 log(SELF, Level.SEVERE, msg, null); 480 } 481 } 482 483 /** 484 * Log a message at the SEVERE level according to the specified format and 485 * argument. 486 * 487 * <p> 488 * This form avoids superfluous object creation when the logger is disabled 489 * for the SEVERE level. 490 * </p> 491 * 492 * @param format 493 * the format string 494 * @param arg 495 * the argument 496 */ 497 public void error(String format, Object arg) { 498 if (logger.isLoggable(Level.SEVERE)) { 499 FormattingTuple ft = MessageFormatter.format(format, arg); 500 log(SELF, Level.SEVERE, ft.getMessage(), ft.getThrowable()); 501 } 502 } 503 504 /** 505 * Log a message at the SEVERE level according to the specified format and 506 * arguments. 507 * 508 * <p> 509 * This form avoids superfluous object creation when the logger is disabled 510 * for the SEVERE level. 511 * </p> 512 * 513 * @param format 514 * the format string 515 * @param arg1 516 * the first argument 517 * @param arg2 518 * the second argument 519 */ 520 public void error(String format, Object arg1, Object arg2) { 521 if (logger.isLoggable(Level.SEVERE)) { 522 FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); 523 log(SELF, Level.SEVERE, ft.getMessage(), ft.getThrowable()); 524 } 525 } 526 527 /** 528 * Log a message at level SEVERE according to the specified format and 529 * arguments. 530 * 531 * <p> 532 * This form avoids superfluous object creation when the logger is disabled 533 * for the SEVERE level. 534 * </p> 535 * 536 * @param format 537 * the format string 538 * @param arguments 539 * an array of arguments 540 */ 541 public void error(String format, Object... arguments) { 542 if (logger.isLoggable(Level.SEVERE)) { 543 FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments); 544 log(SELF, Level.SEVERE, ft.getMessage(), ft.getThrowable()); 545 } 546 } 547 548 /** 549 * Log an exception (throwable) at the SEVERE level with an accompanying 550 * message. 551 * 552 * @param msg 553 * the message accompanying the exception 554 * @param t 555 * the exception (throwable) to log 556 */ 557 public void error(String msg, Throwable t) { 558 if (logger.isLoggable(Level.SEVERE)) { 559 log(SELF, Level.SEVERE, msg, t); 560 } 561 } 562 563 /** 564 * Log the message at the specified level with the specified throwable if any. 565 * This method creates a LogRecord and fills in caller date before calling 566 * this instance's JDK14 logger. 567 * 568 * See bug report #13 for more details. 569 * 570 * @param level 571 * @param msg 572 * @param t 573 */ 574 private void log(String callerFQCN, Level level, String msg, Throwable t) { 575 // millis and thread are filled by the constructor 576 LogRecord record = new LogRecord(level, msg); 577 record.setLoggerName(getName()); 578 record.setThrown(t); 579 // Note: parameters in record are not set because SLF4J only 580 // supports a single formatting style 581 fillCallerData(callerFQCN, record); 582 logger.log(record); 583 } 584 585 static String SELF = JDK14LoggerAdapter.class.getName(); 586 static String SUPER = MarkerIgnoringBase.class.getName(); 587 588 /** 589 * Fill in caller data if possible. 590 * 591 * @param record 592 * The record to update 593 */ 594 final private void fillCallerData(String callerFQCN, LogRecord record) { 595 StackTraceElement[] steArray = new Throwable().getStackTrace(); 596 597 int selfIndex = -1; 598 for (int i = 0; i < steArray.length; i++) { 599 final String className = steArray[i].getClassName(); 600 if (className.equals(callerFQCN) || className.equals(SUPER)) { 601 selfIndex = i; 602 break; 603 } 604 } 605 606 int found = -1; 607 for (int i = selfIndex + 1; i < steArray.length; i++) { 608 final String className = steArray[i].getClassName(); 609 if (!(className.equals(callerFQCN) || className.equals(SUPER))) { 610 found = i; 611 break; 612 } 613 } 614 615 if (found != -1) { 616 StackTraceElement ste = steArray[found]; 617 // setting the class name has the side effect of setting 618 // the needToInferCaller variable to false. 619 record.setSourceClassName(ste.getClassName()); 620 record.setSourceMethodName(ste.getMethodName()); 621 } 622 } 623 624 public void log(Marker marker, String callerFQCN, int level, String message, Object[] argArray, Throwable t) { 625 Level julLevel = slf4jLevelIntToJULLevel(level); 626 // the logger.isLoggable check avoids the unconditional 627 // construction of location data for disabled log 628 // statements. As of 2008-07-31, callers of this method 629 // do not perform this check. See also 630 // http://jira.qos.ch/browse/SLF4J-81 631 if (logger.isLoggable(julLevel)) { 632 log(callerFQCN, julLevel, message, t); 633 } 634 } 635 636 private Level slf4jLevelIntToJULLevel(int slf4jLevelInt) { 637 Level julLevel; 638 switch (slf4jLevelInt) { 639 case LocationAwareLogger.TRACE_INT: 640 julLevel = Level.FINEST; 641 break; 642 case LocationAwareLogger.DEBUG_INT: 643 julLevel = Level.FINE; 644 break; 645 case LocationAwareLogger.INFO_INT: 646 julLevel = Level.INFO; 647 break; 648 case LocationAwareLogger.WARN_INT: 649 julLevel = Level.WARNING; 650 break; 651 case LocationAwareLogger.ERROR_INT: 652 julLevel = Level.SEVERE; 653 break; 654 default: 655 throw new IllegalStateException("Level number " + slf4jLevelInt + " is not recognized."); 656 } 657 return julLevel; 658 } 659 660 /** 661 * @since 1.7.15 662 */ 663 public void log(LoggingEvent event) { 664 Level julLevel = slf4jLevelIntToJULLevel(event.getLevel().toInt()); 665 if (logger.isLoggable(julLevel)) { 666 LogRecord record = eventToRecord(event, julLevel); 667 logger.log(record); 668 } 669 } 670 671 private LogRecord eventToRecord(LoggingEvent event, Level julLevel) { 672 String format = event.getMessage(); 673 Object[] arguments = event.getArgumentArray(); 674 FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments); 675 if (ft.getThrowable() != null && event.getThrowable() != null) { 676 throw new IllegalArgumentException("both last element in argument array and last argument are of type Throwable"); 677 } 678 679 Throwable t = event.getThrowable(); 680 if (ft.getThrowable() != null) { 681 t = ft.getThrowable(); 682 throw new IllegalStateException("fix above code"); 683 } 684 685 LogRecord record = new LogRecord(julLevel, ft.getMessage()); 686 record.setLoggerName(event.getLoggerName()); 687 record.setMillis(event.getTimeStamp()); 688 record.setSourceClassName(EventConstants.NA_SUBST); 689 record.setSourceMethodName(EventConstants.NA_SUBST); 690 691 record.setThrown(t); 692 return record; 693 } 694}