1   /*
2    * Copyright (c) 2004-2005 SLF4J.ORG
3    * Copyright (c) 2004-2005 QOS.ch
4    *
5    * All rights reserved.
6    *
7    * Permission is hereby granted, free of charge, to any person obtaining
8    * a copy of this software and associated documentation files (the
9    * "Software"), to  deal in  the Software without  restriction, including
10   * without limitation  the rights to  use, copy, modify,  merge, publish,
11   * distribute, and/or sell copies of  the Software, and to permit persons
12   * to whom  the Software is furnished  to do so, provided  that the above
13   * copyright notice(s) and this permission notice appear in all copies of
14   * the  Software and  that both  the above  copyright notice(s)  and this
15   * permission notice appear in supporting documentation.
16   *
17   * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
18   * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
19   * MERCHANTABILITY, FITNESS FOR  A PARTICULAR PURPOSE AND NONINFRINGEMENT
20   * OF  THIRD PARTY  RIGHTS. IN  NO EVENT  SHALL THE  COPYRIGHT  HOLDER OR
21   * HOLDERS  INCLUDED IN  THIS  NOTICE BE  LIABLE  FOR ANY  CLAIM, OR  ANY
22   * SPECIAL INDIRECT  OR CONSEQUENTIAL DAMAGES, OR  ANY DAMAGES WHATSOEVER
23   * RESULTING FROM LOSS  OF USE, DATA OR PROFITS, WHETHER  IN AN ACTION OF
24   * CONTRACT, NEGLIGENCE  OR OTHER TORTIOUS  ACTION, ARISING OUT OF  OR IN
25   * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26   *
27   * Except as  contained in  this notice, the  name of a  copyright holder
28   * shall not be used in advertising or otherwise to promote the sale, use
29   * or other dealings in this Software without prior written authorization
30   * of the copyright holder.
31   *
32   */
33  
34  package org.slf4j.impl;
35  
36  import java.util.logging.Level;
37  import java.util.logging.LogRecord;
38  
39  import org.slf4j.Logger;
40  import org.slf4j.Marker;
41  import org.slf4j.helpers.FormattingTuple;
42  import org.slf4j.helpers.MarkerIgnoringBase;
43  import org.slf4j.helpers.MessageFormatter;
44  import org.slf4j.spi.LocationAwareLogger;
45  
46  /**
47   * A wrapper over {@link java.util.logging.Logger java.util.logging.Logger} in
48   * conformity with the {@link Logger} interface. Note that the logging levels
49   * mentioned in this class refer to those defined in the java.util.logging
50   * package.
51   * 
52   * @author Ceki Gülcü
53   * @author Peter Royal
54   */
55  public final class JDK14LoggerAdapter extends MarkerIgnoringBase implements
56      LocationAwareLogger {
57  
58    private static final long serialVersionUID = -8053026990503422791L;
59  
60    final java.util.logging.Logger logger;
61  
62    // WARN: JDK14LoggerAdapter constructor should have only package access so
63    // that only JDK14LoggerFactory be able to create one.
64    JDK14LoggerAdapter(java.util.logging.Logger logger) {
65      this.logger = logger;
66      this.name = logger.getName();
67    }
68  
69    /**
70     * Is this logger instance enabled for the FINEST level?
71     * 
72     * @return True if this Logger is enabled for level FINEST, false otherwise.
73     */
74    public boolean isTraceEnabled() {
75      return logger.isLoggable(Level.FINEST);
76    }
77  
78    /**
79     * Log a message object at level FINEST.
80     * 
81     * @param msg
82     *          - the message object to be logged
83     */
84    public void trace(String msg) {
85      if (logger.isLoggable(Level.FINEST)) {
86        log(SELF, Level.FINEST, msg, null);
87      }
88    }
89  
90    /**
91     * Log a message at level FINEST according to the specified format and
92     * argument.
93     * 
94     * <p>
95     * This form avoids superfluous object creation when the logger is disabled
96     * for level FINEST.
97     * </p>
98     * 
99     * @param format
100    *          the format string
101    * @param arg
102    *          the argument
103    */
104   public void trace(String format, Object arg) {
105     if (logger.isLoggable(Level.FINEST)) {
106       FormattingTuple ft = MessageFormatter.format(format, arg);
107       log(SELF, Level.FINEST, ft.getMessage(), ft.getThrowable());
108     }
109   }
110 
111   /**
112    * Log a message at level FINEST according to the specified format and
113    * arguments.
114    * 
115    * <p>
116    * This form avoids superfluous object creation when the logger is disabled
117    * for the FINEST level.
118    * </p>
119    * 
120    * @param format
121    *          the format string
122    * @param arg1
123    *          the first argument
124    * @param arg2
125    *          the second argument
126    */
127   public void trace(String format, Object arg1, Object arg2) {
128     if (logger.isLoggable(Level.FINEST)) {
129       FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
130       log(SELF, Level.FINEST, ft.getMessage(), ft.getThrowable());
131     }
132   }
133 
134   /**
135    * Log a message at level FINEST according to the specified format and
136    * arguments.
137    * 
138    * <p>
139    * This form avoids superfluous object creation when the logger is disabled
140    * for the FINEST level.
141    * </p>
142    * 
143    * @param format
144    *          the format string
145    * @param argArray
146    *          an array of arguments
147    */
148   public void trace(String format, Object[] argArray) {
149     if (logger.isLoggable(Level.FINEST)) {
150       FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
151       log(SELF, Level.FINEST, ft.getMessage(), ft.getThrowable());
152     }
153   }
154 
155   /**
156    * Log an exception (throwable) at level FINEST with an accompanying message.
157    * 
158    * @param msg
159    *          the message accompanying the exception
160    * @param t
161    *          the exception (throwable) to log
162    */
163   public void trace(String msg, Throwable t) {
164     if (logger.isLoggable(Level.FINEST)) {
165       log(SELF, Level.FINEST, msg, t);
166     }
167   }
168 
169   /**
170    * Is this logger instance enabled for the FINE level?
171    * 
172    * @return True if this Logger is enabled for level FINE, false otherwise.
173    */
174   public boolean isDebugEnabled() {
175     return logger.isLoggable(Level.FINE);
176   }
177 
178   /**
179    * Log a message object at level FINE.
180    * 
181    * @param msg
182    *          - the message object to be logged
183    */
184   public void debug(String msg) {
185     if (logger.isLoggable(Level.FINE)) {
186       log(SELF, Level.FINE, msg, null);
187     }
188   }
189 
190   /**
191    * Log a message at level FINE according to the specified format and argument.
192    * 
193    * <p>
194    * This form avoids superfluous object creation when the logger is disabled
195    * for level FINE.
196    * </p>
197    * 
198    * @param format
199    *          the format string
200    * @param arg
201    *          the argument
202    */
203   public void debug(String format, Object arg) {
204     if (logger.isLoggable(Level.FINE)) {
205       FormattingTuple ft = MessageFormatter.format(format, arg);
206       log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable());
207     }
208   }
209 
210   /**
211    * Log a message at level FINE according to the specified format and
212    * arguments.
213    * 
214    * <p>
215    * This form avoids superfluous object creation when the logger is disabled
216    * for the FINE level.
217    * </p>
218    * 
219    * @param format
220    *          the format string
221    * @param arg1
222    *          the first argument
223    * @param arg2
224    *          the second argument
225    */
226   public void debug(String format, Object arg1, Object arg2) {
227     if (logger.isLoggable(Level.FINE)) {
228       FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
229       log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable());
230     }
231   }
232 
233   /**
234    * Log a message at level FINE according to the specified format and
235    * arguments.
236    * 
237    * <p>
238    * This form avoids superfluous object creation when the logger is disabled
239    * for the FINE level.
240    * </p>
241    * 
242    * @param format
243    *          the format string
244    * @param argArray
245    *          an array of arguments
246    */
247   public void debug(String format, Object[] argArray) {
248     if (logger.isLoggable(Level.FINE)) {
249       FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
250       log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable());
251     }
252   }
253 
254   /**
255    * Log an exception (throwable) at level FINE with an accompanying message.
256    * 
257    * @param msg
258    *          the message accompanying the exception
259    * @param t
260    *          the exception (throwable) to log
261    */
262   public void debug(String msg, Throwable t) {
263     if (logger.isLoggable(Level.FINE)) {
264       log(SELF, Level.FINE, msg, t);
265     }
266   }
267 
268   /**
269    * Is this logger instance enabled for the INFO level?
270    * 
271    * @return True if this Logger is enabled for the INFO level, false otherwise.
272    */
273   public boolean isInfoEnabled() {
274     return logger.isLoggable(Level.INFO);
275   }
276 
277   /**
278    * Log a message object at the INFO level.
279    * 
280    * @param msg
281    *          - the message object to be logged
282    */
283   public void info(String msg) {
284     if (logger.isLoggable(Level.INFO)) {
285       log(SELF, Level.INFO, msg, null);
286     }
287   }
288 
289   /**
290    * Log a message at level INFO according to the specified format and argument.
291    * 
292    * <p>
293    * This form avoids superfluous object creation when the logger is disabled
294    * for the INFO level.
295    * </p>
296    * 
297    * @param format
298    *          the format string
299    * @param arg
300    *          the argument
301    */
302   public void info(String format, Object arg) {
303     if (logger.isLoggable(Level.INFO)) {
304       FormattingTuple ft = MessageFormatter.format(format, arg);
305       log(SELF, Level.INFO, ft.getMessage(), ft.getThrowable());
306     }
307   }
308 
309   /**
310    * Log a message at the INFO level according to the specified format and
311    * arguments.
312    * 
313    * <p>
314    * This form avoids superfluous object creation when the logger is disabled
315    * for the INFO level.
316    * </p>
317    * 
318    * @param format
319    *          the format string
320    * @param arg1
321    *          the first argument
322    * @param arg2
323    *          the second argument
324    */
325   public void info(String format, Object arg1, Object arg2) {
326     if (logger.isLoggable(Level.INFO)) {
327       FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
328       log(SELF, Level.INFO, ft.getMessage(), ft.getThrowable());
329     }
330   }
331 
332   /**
333    * Log a message at level INFO according to the specified format and
334    * arguments.
335    * 
336    * <p>
337    * This form avoids superfluous object creation when the logger is disabled
338    * for the INFO level.
339    * </p>
340    * 
341    * @param format
342    *          the format string
343    * @param argArray
344    *          an array of arguments
345    */
346   public void info(String format, Object[] argArray) {
347     if (logger.isLoggable(Level.INFO)) {
348       FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
349       log(SELF, Level.INFO, ft.getMessage(), ft.getThrowable());
350     }
351   }
352 
353   /**
354    * Log an exception (throwable) at the INFO level with an accompanying
355    * message.
356    * 
357    * @param msg
358    *          the message accompanying the exception
359    * @param t
360    *          the exception (throwable) to log
361    */
362   public void info(String msg, Throwable t) {
363     if (logger.isLoggable(Level.INFO)) {
364       log(SELF, Level.INFO, msg, t);
365     }
366   }
367 
368   /**
369    * Is this logger instance enabled for the WARNING level?
370    * 
371    * @return True if this Logger is enabled for the WARNING level, false
372    *         otherwise.
373    */
374   public boolean isWarnEnabled() {
375     return logger.isLoggable(Level.WARNING);
376   }
377 
378   /**
379    * Log a message object at the WARNING level.
380    * 
381    * @param msg
382    *          - the message object to be logged
383    */
384   public void warn(String msg) {
385     if (logger.isLoggable(Level.WARNING)) {
386       log(SELF, Level.WARNING, msg, null);
387     }
388   }
389 
390   /**
391    * Log a message at the WARNING level according to the specified format and
392    * argument.
393    * 
394    * <p>
395    * This form avoids superfluous object creation when the logger is disabled
396    * for the WARNING level.
397    * </p>
398    * 
399    * @param format
400    *          the format string
401    * @param arg
402    *          the argument
403    */
404   public void warn(String format, Object arg) {
405     if (logger.isLoggable(Level.WARNING)) {
406       FormattingTuple ft = MessageFormatter.format(format, arg);
407       log(SELF, Level.WARNING, ft.getMessage(), ft.getThrowable());
408     }
409   }
410 
411   /**
412    * Log a message at the WARNING level according to the specified format and
413    * arguments.
414    * 
415    * <p>
416    * This form avoids superfluous object creation when the logger is disabled
417    * for the WARNING level.
418    * </p>
419    * 
420    * @param format
421    *          the format string
422    * @param arg1
423    *          the first argument
424    * @param arg2
425    *          the second argument
426    */
427   public void warn(String format, Object arg1, Object arg2) {
428     if (logger.isLoggable(Level.WARNING)) {
429       FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
430       log(SELF, Level.WARNING, ft.getMessage(), ft.getThrowable());
431     }
432   }
433 
434   /**
435    * Log a message at level WARNING according to the specified format and
436    * arguments.
437    * 
438    * <p>
439    * This form avoids superfluous object creation when the logger is disabled
440    * for the WARNING level.
441    * </p>
442    * 
443    * @param format
444    *          the format string
445    * @param argArray
446    *          an array of arguments
447    */
448   public void warn(String format, Object[] argArray) {
449     if (logger.isLoggable(Level.WARNING)) {
450       FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
451       log(SELF, Level.WARNING, ft.getMessage(), ft.getThrowable());
452     }
453   }
454 
455   /**
456    * Log an exception (throwable) at the WARNING level with an accompanying
457    * message.
458    * 
459    * @param msg
460    *          the message accompanying the exception
461    * @param t
462    *          the exception (throwable) to log
463    */
464   public void warn(String msg, Throwable t) {
465     if (logger.isLoggable(Level.WARNING)) {
466       log(SELF, Level.WARNING, msg, t);
467     }
468   }
469 
470   /**
471    * Is this logger instance enabled for level SEVERE?
472    * 
473    * @return True if this Logger is enabled for level SEVERE, false otherwise.
474    */
475   public boolean isErrorEnabled() {
476     return logger.isLoggable(Level.SEVERE);
477   }
478 
479   /**
480    * Log a message object at the SEVERE level.
481    * 
482    * @param msg
483    *          - the message object to be logged
484    */
485   public void error(String msg) {
486     if (logger.isLoggable(Level.SEVERE)) {
487       log(SELF, Level.SEVERE, msg, null);
488     }
489   }
490 
491   /**
492    * Log a message at the SEVERE level according to the specified format and
493    * argument.
494    * 
495    * <p>
496    * This form avoids superfluous object creation when the logger is disabled
497    * for the SEVERE level.
498    * </p>
499    * 
500    * @param format
501    *          the format string
502    * @param arg
503    *          the argument
504    */
505   public void error(String format, Object arg) {
506     if (logger.isLoggable(Level.SEVERE)) {
507       FormattingTuple ft = MessageFormatter.format(format, arg);
508       log(SELF, Level.SEVERE, ft.getMessage(), ft.getThrowable());
509     }
510   }
511 
512   /**
513    * Log a message at the SEVERE level according to the specified format and
514    * arguments.
515    * 
516    * <p>
517    * This form avoids superfluous object creation when the logger is disabled
518    * for the SEVERE level.
519    * </p>
520    * 
521    * @param format
522    *          the format string
523    * @param arg1
524    *          the first argument
525    * @param arg2
526    *          the second argument
527    */
528   public void error(String format, Object arg1, Object arg2) {
529     if (logger.isLoggable(Level.SEVERE)) {
530       FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
531       log(SELF, Level.SEVERE, ft.getMessage(), ft.getThrowable());
532     }
533   }
534 
535   /**
536    * Log a message at level SEVERE according to the specified format and
537    * arguments.
538    * 
539    * <p>
540    * This form avoids superfluous object creation when the logger is disabled
541    * for the SEVERE level.
542    * </p>
543    * 
544    * @param format
545    *          the format string
546    * @param argArray
547    *          an array of arguments
548    */
549   public void error(String format, Object[] argArray) {
550     if (logger.isLoggable(Level.SEVERE)) {
551       FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
552       log(SELF, Level.SEVERE, ft.getMessage(), ft.getThrowable());
553     }
554   }
555 
556   /**
557    * Log an exception (throwable) at the SEVERE level with an accompanying
558    * message.
559    * 
560    * @param msg
561    *          the message accompanying the exception
562    * @param t
563    *          the exception (throwable) to log
564    */
565   public void error(String msg, Throwable t) {
566     if (logger.isLoggable(Level.SEVERE)) {
567       log(SELF, Level.SEVERE, msg, t);
568     }
569   }
570 
571   /**
572    * Log the message at the specified level with the specified throwable if any.
573    * This method creates a LogRecord and fills in caller date before calling
574    * this instance's JDK14 logger.
575    * 
576    * See bug report #13 for more details.
577    * 
578    * @param level
579    * @param msg
580    * @param t
581    */
582   private void log(String callerFQCN, Level level, String msg, Throwable t) {
583     // millis and thread are filled by the constructor
584     LogRecord record = new LogRecord(level, msg);
585     record.setLoggerName(getName());
586     record.setThrown(t);
587     fillCallerData(callerFQCN, record);
588     logger.log(record);
589 
590   }
591 
592   static String SELF = JDK14LoggerAdapter.class.getName();
593   static String SUPER = MarkerIgnoringBase.class.getName();
594 
595   /**
596    * Fill in caller data if possible.
597    * 
598    * @param record
599    *          The record to update
600    */
601   final private void fillCallerData(String callerFQCN, LogRecord record) {
602     StackTraceElement[] steArray = new Throwable().getStackTrace();
603 
604     int selfIndex = -1;
605     for (int i = 0; i < steArray.length; i++) {
606       final String className = steArray[i].getClassName();
607       if (className.equals(callerFQCN) || className.equals(SUPER)) {
608         selfIndex = i;
609         break;
610       }
611     }
612 
613     int found = -1;
614     for (int i = selfIndex + 1; i < steArray.length; i++) {
615       final String className = steArray[i].getClassName();
616       if (!(className.equals(callerFQCN) || className.equals(SUPER))) {
617         found = i;
618         break;
619       }
620     }
621 
622     if (found != -1) {
623       StackTraceElement ste = steArray[found];
624       // setting the class name has the side effect of setting
625       // the needToInferCaller variable to false.
626       record.setSourceClassName(ste.getClassName());
627       record.setSourceMethodName(ste.getMethodName());
628     }
629   }
630 
631   public void log(Marker marker, String callerFQCN, int level, String message,
632       Object[] argArray, Throwable t) {
633     Level julLevel;
634     switch (level) {
635     case LocationAwareLogger.TRACE_INT:
636       julLevel = Level.FINEST;
637       break;
638     case LocationAwareLogger.DEBUG_INT:
639       julLevel = Level.FINE;
640       break;
641     case LocationAwareLogger.INFO_INT:
642       julLevel = Level.INFO;
643       break;
644     case LocationAwareLogger.WARN_INT:
645       julLevel = Level.WARNING;
646       break;
647     case LocationAwareLogger.ERROR_INT:
648       julLevel = Level.SEVERE;
649       break;
650     default:
651       throw new IllegalStateException("Level number " + level
652           + " is not recognized.");
653     }
654     // the logger.isLoggable check avoids the unconditional
655     // construction of location data for disabled log
656     // statements. As of 2008-07-31, callers of this method
657     // do not perform this check. See also
658     // http://bugzilla.slf4j.org/show_bug.cgi?id=90
659     if (logger.isLoggable(julLevel)) {
660       log(callerFQCN, julLevel, message, t);
661     }
662   }
663 }