    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&uuml;lc&uuml;
    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}