View Javadoc

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 }