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.jul;
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.helpers.SubstituteLogger;
038import org.slf4j.spi.LocationAwareLogger;
039
040/**
041 * A wrapper over {@link java.util.logging.Logger java.util.logging.Logger} in
042 * conformity with the {@link Logger} interface. Note that the logging levels
043 * mentioned in this class refer to those defined in the java.util.logging
044 * package.
045 * 
046 * @author Ceki Gülcü
047 * @author Peter Royal
048 */
049public final class JDK14LoggerAdapter extends MarkerIgnoringBase implements LocationAwareLogger {
050
051    private static final long serialVersionUID = -8053026990503422791L;
052
053    transient final java.util.logging.Logger logger;
054
055    // WARN: JDK14LoggerAdapter constructor should have only package access so
056    // that only JDK14LoggerFactory be able to create one.
057    JDK14LoggerAdapter(java.util.logging.Logger logger) {
058        this.logger = logger;
059        this.name = logger.getName();
060    }
061
062    /**
063     * Is this logger instance enabled for the FINEST level?
064     * 
065     * @return True if this Logger is enabled for level FINEST, false otherwise.
066     */
067    public boolean isTraceEnabled() {
068        return logger.isLoggable(Level.FINEST);
069    }
070
071    /**
072     * Log a message object at level FINEST.
073     * 
074     * @param msg
075     *          - the message object to be logged
076     */
077    public void trace(String msg) {
078        if (logger.isLoggable(Level.FINEST)) {
079            log(SELF, Level.FINEST, msg, null);
080        }
081    }
082
083    /**
084     * Log a message at level FINEST according to the specified format and
085     * argument.
086     * 
087     * <p>
088     * This form avoids superfluous object creation when the logger is disabled
089     * for level FINEST.
090     * 
091     * 
092     * @param format
093     *          the format string
094     * @param arg
095     *          the argument
096     */
097    public void trace(String format, Object arg) {
098        if (logger.isLoggable(Level.FINEST)) {
099            FormattingTuple ft = MessageFormatter.format(format, arg);
100            log(SELF, Level.FINEST, ft.getMessage(), ft.getThrowable());
101        }
102    }
103
104    /**
105     * Log a message at level FINEST according to the specified format and
106     * arguments.
107     * 
108     * <p>
109     * This form avoids superfluous object creation when the logger is disabled
110     * for the FINEST level.
111     * 
112     * 
113     * @param format
114     *          the format string
115     * @param arg1
116     *          the first argument
117     * @param arg2
118     *          the second argument
119     */
120    public void trace(String format, Object arg1, Object arg2) {
121        if (logger.isLoggable(Level.FINEST)) {
122            FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
123            log(SELF, Level.FINEST, ft.getMessage(), ft.getThrowable());
124        }
125    }
126
127    /**
128     * Log a message at level FINEST according to the specified format and
129     * arguments.
130     * 
131     * <p>
132     * This form avoids superfluous object creation when the logger is disabled
133     * for the FINEST level.
134     * 
135     * 
136     * @param format
137     *          the format string
138     * @param argArray
139     *          an array of arguments
140     */
141    public void trace(String format, Object... argArray) {
142        if (logger.isLoggable(Level.FINEST)) {
143            FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
144            log(SELF, Level.FINEST, ft.getMessage(), ft.getThrowable());
145        }
146    }
147
148    /**
149     * Log an exception (throwable) at level FINEST with an accompanying message.
150     * 
151     * @param msg
152     *          the message accompanying the exception
153     * @param t
154     *          the exception (throwable) to log
155     */
156    public void trace(String msg, Throwable t) {
157        if (logger.isLoggable(Level.FINEST)) {
158            log(SELF, Level.FINEST, msg, t);
159        }
160    }
161
162    /**
163     * Is this logger instance enabled for the FINE level?
164     * 
165     * @return True if this Logger is enabled for level FINE, false otherwise.
166     */
167    public boolean isDebugEnabled() {
168        return logger.isLoggable(Level.FINE);
169    }
170
171    /**
172     * Log a message object at level FINE.
173     * 
174     * @param msg
175     *          - the message object to be logged
176     */
177    public void debug(String msg) {
178        if (logger.isLoggable(Level.FINE)) {
179            log(SELF, Level.FINE, msg, null);
180        }
181    }
182
183    /**
184     * Log a message at level FINE according to the specified format and argument.
185     * 
186     * <p>
187     * This form avoids superfluous object creation when the logger is disabled
188     * for level FINE.
189     * 
190     * 
191     * @param format
192     *          the format string
193     * @param arg
194     *          the argument
195     */
196    public void debug(String format, Object arg) {
197        if (logger.isLoggable(Level.FINE)) {
198            FormattingTuple ft = MessageFormatter.format(format, arg);
199            log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable());
200        }
201    }
202
203    /**
204     * Log a message at level FINE according to the specified format and
205     * arguments.
206     * 
207     * <p>
208     * This form avoids superfluous object creation when the logger is disabled
209     * for the FINE level.
210     * 
211     * 
212     * @param format
213     *          the format string
214     * @param arg1
215     *          the first argument
216     * @param arg2
217     *          the second argument
218     */
219    public void debug(String format, Object arg1, Object arg2) {
220        if (logger.isLoggable(Level.FINE)) {
221            FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
222            log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable());
223        }
224    }
225
226    /**
227     * Log a message at level FINE according to the specified format and
228     * arguments.
229     * 
230     * <p>
231     * This form avoids superfluous object creation when the logger is disabled
232     * for the FINE level.
233     * 
234     * 
235     * @param format
236     *          the format string
237     * @param argArray
238     *          an array of arguments
239     */
240    public void debug(String format, Object... argArray) {
241        if (logger.isLoggable(Level.FINE)) {
242            FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
243            log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable());
244        }
245    }
246
247    /**
248     * Log an exception (throwable) at level FINE with an accompanying message.
249     * 
250     * @param msg
251     *          the message accompanying the exception
252     * @param t
253     *          the exception (throwable) to log
254     */
255    public void debug(String msg, Throwable t) {
256        if (logger.isLoggable(Level.FINE)) {
257            log(SELF, Level.FINE, msg, t);
258        }
259    }
260
261    /**
262     * Is this logger instance enabled for the INFO level?
263     * 
264     * @return True if this Logger is enabled for the INFO level, false otherwise.
265     */
266    public boolean isInfoEnabled() {
267        return logger.isLoggable(Level.INFO);
268    }
269
270    /**
271     * Log a message object at the INFO level.
272     * 
273     * @param msg
274     *          - the message object to be logged
275     */
276    public void info(String msg) {
277        if (logger.isLoggable(Level.INFO)) {
278            log(SELF, Level.INFO, msg, null);
279        }
280    }
281
282    /**
283     * Log a message at level INFO according to the specified format and argument.
284     * 
285     * <p>
286     * This form avoids superfluous object creation when the logger is disabled
287     * for the INFO level.
288     * 
289     * 
290     * @param format
291     *          the format string
292     * @param arg
293     *          the argument
294     */
295    public void info(String format, Object arg) {
296        if (logger.isLoggable(Level.INFO)) {
297            FormattingTuple ft = MessageFormatter.format(format, arg);
298            log(SELF, Level.INFO, ft.getMessage(), ft.getThrowable());
299        }
300    }
301
302    /**
303     * Log a message at the INFO level according to the specified format and
304     * arguments.
305     * 
306     * <p>
307     * This form avoids superfluous object creation when the logger is disabled
308     * for the INFO level.
309     * 
310     * 
311     * @param format
312     *          the format string
313     * @param arg1
314     *          the first argument
315     * @param arg2
316     *          the second argument
317     */
318    public void info(String format, Object arg1, Object arg2) {
319        if (logger.isLoggable(Level.INFO)) {
320            FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
321            log(SELF, Level.INFO, ft.getMessage(), ft.getThrowable());
322        }
323    }
324
325    /**
326     * Log a message at level INFO according to the specified format and
327     * arguments.
328     * 
329     * <p>
330     * This form avoids superfluous object creation when the logger is disabled
331     * for the INFO level.
332     * 
333     * 
334     * @param format
335     *          the format string
336     * @param argArray
337     *          an array of arguments
338     */
339    public void info(String format, Object... argArray) {
340        if (logger.isLoggable(Level.INFO)) {
341            FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
342            log(SELF, Level.INFO, ft.getMessage(), ft.getThrowable());
343        }
344    }
345
346    /**
347     * Log an exception (throwable) at the INFO level with an accompanying
348     * message.
349     * 
350     * @param msg
351     *          the message accompanying the exception
352     * @param t
353     *          the exception (throwable) to log
354     */
355    public void info(String msg, Throwable t) {
356        if (logger.isLoggable(Level.INFO)) {
357            log(SELF, Level.INFO, msg, t);
358        }
359    }
360
361    /**
362     * Is this logger instance enabled for the WARNING level?
363     * 
364     * @return True if this Logger is enabled for the WARNING level, false
365     *         otherwise.
366     */
367    public boolean isWarnEnabled() {
368        return logger.isLoggable(Level.WARNING);
369    }
370
371    /**
372     * Log a message object at the WARNING level.
373     * 
374     * @param msg
375     *          - the message object to be logged
376     */
377    public void warn(String msg) {
378        if (logger.isLoggable(Level.WARNING)) {
379            log(SELF, Level.WARNING, msg, null);
380        }
381    }
382
383    /**
384     * Log a message at the WARNING level according to the specified format and
385     * argument.
386     * 
387     * <p>
388     * This form avoids superfluous object creation when the logger is disabled
389     * for the WARNING level.
390     * 
391     * 
392     * @param format
393     *          the format string
394     * @param arg
395     *          the argument
396     */
397    public void warn(String format, Object arg) {
398        if (logger.isLoggable(Level.WARNING)) {
399            FormattingTuple ft = MessageFormatter.format(format, arg);
400            log(SELF, Level.WARNING, ft.getMessage(), ft.getThrowable());
401        }
402    }
403
404    /**
405     * Log a message at the WARNING level according to the specified format and
406     * arguments.
407     * 
408     * <p>
409     * This form avoids superfluous object creation when the logger is disabled
410     * for the WARNING level.
411     * 
412     * 
413     * @param format
414     *          the format string
415     * @param arg1
416     *          the first argument
417     * @param arg2
418     *          the second argument
419     */
420    public void warn(String format, Object arg1, Object arg2) {
421        if (logger.isLoggable(Level.WARNING)) {
422            FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
423            log(SELF, Level.WARNING, ft.getMessage(), ft.getThrowable());
424        }
425    }
426
427    /**
428     * Log a message at level WARNING according to the specified format and
429     * arguments.
430     * 
431     * <p>
432     * This form avoids superfluous object creation when the logger is disabled
433     * for the WARNING level.
434     * 
435     * 
436     * @param format
437     *          the format string
438     * @param argArray
439     *          an array of arguments
440     */
441    public void warn(String format, Object... argArray) {
442        if (logger.isLoggable(Level.WARNING)) {
443            FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
444            log(SELF, Level.WARNING, ft.getMessage(), ft.getThrowable());
445        }
446    }
447
448    /**
449     * Log an exception (throwable) at the WARNING level with an accompanying
450     * message.
451     * 
452     * @param msg
453     *          the message accompanying the exception
454     * @param t
455     *          the exception (throwable) to log
456     */
457    public void warn(String msg, Throwable t) {
458        if (logger.isLoggable(Level.WARNING)) {
459            log(SELF, Level.WARNING, msg, t);
460        }
461    }
462
463    /**
464     * Is this logger instance enabled for level SEVERE?
465     * 
466     * @return True if this Logger is enabled for level SEVERE, false otherwise.
467     */
468    public boolean isErrorEnabled() {
469        return logger.isLoggable(Level.SEVERE);
470    }
471
472    /**
473     * Log a message object at the SEVERE level.
474     * 
475     * @param msg
476     *          - the message object to be logged
477     */
478    public void error(String msg) {
479        if (logger.isLoggable(Level.SEVERE)) {
480            log(SELF, Level.SEVERE, msg, null);
481        }
482    }
483
484    /**
485     * Log a message at the SEVERE level according to the specified format and
486     * argument.
487     * 
488     * <p>
489     * This form avoids superfluous object creation when the logger is disabled
490     * for the SEVERE level.
491     * 
492     * 
493     * @param format
494     *          the format string
495     * @param arg
496     *          the argument
497     */
498    public void error(String format, Object arg) {
499        if (logger.isLoggable(Level.SEVERE)) {
500            FormattingTuple ft = MessageFormatter.format(format, arg);
501            log(SELF, Level.SEVERE, ft.getMessage(), ft.getThrowable());
502        }
503    }
504
505    /**
506     * Log a message at the SEVERE level according to the specified format and
507     * arguments.
508     * 
509     * <p>
510     * This form avoids superfluous object creation when the logger is disabled
511     * for the SEVERE level.
512     * 
513     * 
514     * @param format
515     *          the format string
516     * @param arg1
517     *          the first argument
518     * @param arg2
519     *          the second argument
520     */
521    public void error(String format, Object arg1, Object arg2) {
522        if (logger.isLoggable(Level.SEVERE)) {
523            FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
524            log(SELF, Level.SEVERE, ft.getMessage(), ft.getThrowable());
525        }
526    }
527
528    /**
529     * Log a message at level SEVERE according to the specified format and
530     * arguments.
531     * 
532     * <p>
533     * This form avoids superfluous object creation when the logger is disabled
534     * for the SEVERE level.
535     * 
536     * 
537     * @param format
538     *          the format string
539     * @param arguments
540     *          an array of arguments
541     */
542    public void error(String format, Object... arguments) {
543        if (logger.isLoggable(Level.SEVERE)) {
544            FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments);
545            log(SELF, Level.SEVERE, ft.getMessage(), ft.getThrowable());
546        }
547    }
548
549    /**
550     * Log an exception (throwable) at the SEVERE level with an accompanying
551     * message.
552     * 
553     * @param msg
554     *          the message accompanying the exception
555     * @param t
556     *          the exception (throwable) to log
557     */
558    public void error(String msg, Throwable t) {
559        if (logger.isLoggable(Level.SEVERE)) {
560            log(SELF, Level.SEVERE, msg, t);
561        }
562    }
563
564    /**
565     * Log the message at the specified level with the specified throwable if any.
566     * This method creates a LogRecord and fills in caller date before calling
567     * this instance's JDK14 logger.
568     * 
569     * See bug report #13 for more details.
570     * 
571     * @param level
572     * @param msg
573     * @param t
574     */
575    private void log(String callerFQCN, Level level, String msg, Throwable t) {
576        // millis and thread are filled by the constructor
577        LogRecord record = new LogRecord(level, msg);
578        record.setLoggerName(getName());
579        record.setThrown(t);
580        // Note: parameters in record are not set because SLF4J only
581        // supports a single formatting style
582        fillCallerData(callerFQCN, record);
583        logger.log(record);
584    }
585
586    static String SELF = JDK14LoggerAdapter.class.getName();
587    static String SUPER = MarkerIgnoringBase.class.getName();
588    static String SUBSTITUE = SubstituteLogger.class.getName();
589    
590    static String BARRIER_CLASSES[] = new String[] {SELF, SUPER, SUBSTITUE};
591  
592    /**
593     * Fill in caller data if possible.
594     * 
595     * @param record
596     *          The record to update
597     */
598        final private void fillCallerData(String callerFQCN, LogRecord record) {
599        StackTraceElement[] steArray = new Throwable().getStackTrace();
600
601        int selfIndex = -1;
602        for (int i = 0; i < steArray.length; i++) {
603            final String className = steArray[i].getClassName();
604            
605            if (barrierMatch(callerFQCN, className)) {
606                selfIndex = i;
607                break;
608            }
609        }
610
611        int found = -1;
612        for (int i = selfIndex + 1; i < steArray.length; i++) {
613            final String className = steArray[i].getClassName();
614            if (!(barrierMatch(callerFQCN, className))) {
615                found = i;
616                break;
617            }
618        }
619
620        if (found != -1) {
621            StackTraceElement ste = steArray[found];
622            // setting the class name has the side effect of setting
623            // the needToInferCaller variable to false.
624            record.setSourceClassName(ste.getClassName());
625            record.setSourceMethodName(ste.getMethodName());
626        }
627    }
628
629    private boolean barrierMatch(String callerFQCN, String candidateClassName) {
630        if(candidateClassName.equals(callerFQCN))
631                return true;
632        for(String barrierClassName: BARRIER_CLASSES) {
633                if(barrierClassName.equals(candidateClassName)) {
634                  return true;
635                }
636        }
637        return false;
638        }
639
640        public void log(Marker marker, String callerFQCN, int level, String message, Object[] argArray, Throwable t) {
641        Level julLevel = slf4jLevelIntToJULLevel(level);
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://jira.qos.ch/browse/SLF4J-81
647        if (logger.isLoggable(julLevel)) {
648            log(callerFQCN, julLevel, message, t);
649        }
650    }
651
652    private Level slf4jLevelIntToJULLevel(int slf4jLevelInt) {
653        Level julLevel;
654        switch (slf4jLevelInt) {
655        case LocationAwareLogger.TRACE_INT:
656            julLevel = Level.FINEST;
657            break;
658        case LocationAwareLogger.DEBUG_INT:
659            julLevel = Level.FINE;
660            break;
661        case LocationAwareLogger.INFO_INT:
662            julLevel = Level.INFO;
663            break;
664        case LocationAwareLogger.WARN_INT:
665            julLevel = Level.WARNING;
666            break;
667        case LocationAwareLogger.ERROR_INT:
668            julLevel = Level.SEVERE;
669            break;
670        default:
671            throw new IllegalStateException("Level number " + slf4jLevelInt + " is not recognized.");
672        }
673        return julLevel;
674    }
675
676    /**
677     * @since 1.7.15
678     */
679    public void log(LoggingEvent event) {
680        // assumes that the invocation is made from a substitute logger
681        // this assumption might change in the future with the advent of a fluent API
682        Level julLevel = slf4jLevelIntToJULLevel(event.getLevel().toInt());
683        if (logger.isLoggable(julLevel)) {
684            LogRecord record = eventToRecord(event, julLevel);
685            logger.log(record);
686        }
687    }
688
689    private LogRecord eventToRecord(LoggingEvent event, Level julLevel) {
690        String format = event.getMessage();
691        Object[] arguments = event.getArgumentArray();
692        FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments);
693        if (ft.getThrowable() != null && event.getThrowable() != null) {
694            throw new IllegalArgumentException("both last element in argument array and last argument are of type Throwable");
695        }
696
697        Throwable t = event.getThrowable();
698        if (ft.getThrowable() != null) {
699            t = ft.getThrowable();
700            throw new IllegalStateException("fix above code");
701        }
702
703        LogRecord record = new LogRecord(julLevel, ft.getMessage());
704        record.setLoggerName(event.getLoggerName());
705        record.setMillis(event.getTimeStamp());
706        record.setSourceClassName(EventConstants.NA_SUBST);
707        record.setSourceMethodName(EventConstants.NA_SUBST);
708
709        record.setThrown(t);
710        return record;
711    }
712}