1 /**
2 * Copyright (c) 2004-2011 QOS.ch
3 * All rights reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 *
24 */
25 package org.slf4j.impl;
26
27 import java.util.logging.Level;
28 import java.util.logging.LogRecord;
29
30 import org.slf4j.Logger;
31 import org.slf4j.Marker;
32 import org.slf4j.event.EventConstants;
33 import org.slf4j.event.LoggingEvent;
34 import org.slf4j.helpers.FormattingTuple;
35 import org.slf4j.helpers.MarkerIgnoringBase;
36 import org.slf4j.helpers.MessageFormatter;
37 import org.slf4j.spi.LocationAwareLogger;
38
39 /**
40 * A wrapper over {@link java.util.logging.Logger java.util.logging.Logger} in
41 * conformity with the {@link Logger} interface. Note that the logging levels
42 * mentioned in this class refer to those defined in the java.util.logging
43 * package.
44 *
45 * @author Ceki Gülcü
46 * @author Peter Royal
47 */
48 public final class JDK14LoggerAdapter extends MarkerIgnoringBase implements LocationAwareLogger {
49
50 private static final long serialVersionUID = -8053026990503422791L;
51
52 transient final java.util.logging.Logger logger;
53
54 // WARN: JDK14LoggerAdapter constructor should have only package access so
55 // that only JDK14LoggerFactory be able to create one.
56 JDK14LoggerAdapter(java.util.logging.Logger logger) {
57 this.logger = logger;
58 this.name = logger.getName();
59 }
60
61 /**
62 * Is this logger instance enabled for the FINEST level?
63 *
64 * @return True if this Logger is enabled for level FINEST, false otherwise.
65 */
66 public boolean isTraceEnabled() {
67 return logger.isLoggable(Level.FINEST);
68 }
69
70 /**
71 * Log a message object at level FINEST.
72 *
73 * @param msg
74 * - the message object to be logged
75 */
76 public void trace(String msg) {
77 if (logger.isLoggable(Level.FINEST)) {
78 log(SELF, Level.FINEST, msg, null);
79 }
80 }
81
82 /**
83 * Log a message at level FINEST according to the specified format and
84 * argument.
85 *
86 * <p>
87 * This form avoids superfluous object creation when the logger is disabled
88 * for level FINEST.
89 * </p>
90 *
91 * @param format
92 * the format string
93 * @param arg
94 * the argument
95 */
96 public void trace(String format, Object arg) {
97 if (logger.isLoggable(Level.FINEST)) {
98 FormattingTuple ft = MessageFormatter.format(format, arg);
99 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 }