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