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}