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.log4j12;
026
027import static org.slf4j.event.EventConstants.NA_SUBST;
028
029import java.io.Serializable;
030
031import org.apache.log4j.Level;
032import org.apache.log4j.spi.LocationInfo;
033import org.apache.log4j.spi.ThrowableInformation;
034import org.slf4j.Logger;
035import org.slf4j.Marker;
036import org.slf4j.event.LoggingEvent;
037import org.slf4j.helpers.FormattingTuple;
038import org.slf4j.helpers.MarkerIgnoringBase;
039import org.slf4j.helpers.MessageFormatter;
040import org.slf4j.spi.LocationAwareLogger;
041
042/**
043 * A wrapper over {@link org.apache.log4j.Logger org.apache.log4j.Logger} in
044 * conforming to the {@link Logger} interface.
045 * 
046 * <p>
047 * Note that the logging levels mentioned in this class refer to those defined
048 * in the <a
049 * href="http://logging.apache.org/log4j/docs/api/org/apache/log4j/Level.html">
050 * <code>org.apache.log4j.Level</code></a> class.
051 * 
052 * <p>
053 * The TRACE level was introduced in log4j version 1.2.12. In order to avoid
054 * crashing the host application, in the case the log4j version in use predates
055 * 1.2.12, the TRACE level will be mapped as DEBUG. See also <a
056 * href="http://jira.qos.ch/browse/SLF4J-59">SLF4J-59</a>.
057 * 
058 * @author Ceki G&uuml;lc&uuml;
059 */
060public final class Log4jLoggerAdapter extends MarkerIgnoringBase implements LocationAwareLogger, Serializable {
061
062    private static final long serialVersionUID = 6182834493563598289L;
063
064    final transient org.apache.log4j.Logger logger;
065
066    /**
067     * Following the pattern discussed in pages 162 through 168 of "The complete
068     * log4j manual".
069     */
070    final static String FQCN = Log4jLoggerAdapter.class.getName();
071
072    // Does the log4j version in use recognize the TRACE level?
073    // The trace level was introduced in log4j 1.2.12.
074    final boolean traceCapable;
075
076    // WARN: Log4jLoggerAdapter constructor should have only package access so
077    // that
078    // only Log4jLoggerFactory be able to create one.
079    Log4jLoggerAdapter(org.apache.log4j.Logger logger) {
080        this.logger = logger;
081        this.name = logger.getName();
082        traceCapable = isTraceCapable();
083    }
084
085    private boolean isTraceCapable() {
086        try {
087            logger.isTraceEnabled();
088            return true;
089        } catch (NoSuchMethodError e) {
090            return false;
091        }
092    }
093
094    /**
095     * Is this logger instance enabled for the TRACE level?
096     * 
097     * @return True if this Logger is enabled for level TRACE, false otherwise.
098     */
099    public boolean isTraceEnabled() {
100        if (traceCapable) {
101            return logger.isTraceEnabled();
102        } else {
103            return logger.isDebugEnabled();
104        }
105    }
106
107    /**
108     * Log a message object at level TRACE.
109     * 
110     * @param msg
111     *          - the message object to be logged
112     */
113    public void trace(String msg) {
114        logger.log(FQCN, traceCapable ? Level.TRACE : Level.DEBUG, msg, null);
115    }
116
117    /**
118     * Log a message at level TRACE according to the specified format and
119     * argument.
120     * 
121     * <p>
122     * This form avoids superfluous object creation when the logger is disabled
123     * for level TRACE.
124     * </p>
125     * 
126     * @param format
127     *          the format string
128     * @param arg
129     *          the argument
130     */
131    public void trace(String format, Object arg) {
132        if (isTraceEnabled()) {
133            FormattingTuple ft = MessageFormatter.format(format, arg);
134            logger.log(FQCN, traceCapable ? Level.TRACE : Level.DEBUG, ft.getMessage(), ft.getThrowable());
135        }
136    }
137
138    /**
139     * Log a message at level TRACE according to the specified format and
140     * arguments.
141     * 
142     * <p>
143     * This form avoids superfluous object creation when the logger is disabled
144     * for the TRACE level.
145     * </p>
146     * 
147     * @param format
148     *          the format string
149     * @param arg1
150     *          the first argument
151     * @param arg2
152     *          the second argument
153     */
154    public void trace(String format, Object arg1, Object arg2) {
155        if (isTraceEnabled()) {
156            FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
157            logger.log(FQCN, traceCapable ? Level.TRACE : Level.DEBUG, ft.getMessage(), ft.getThrowable());
158        }
159    }
160
161    /**
162     * Log a message at level TRACE according to the specified format and
163     * arguments.
164     * 
165     * <p>
166     * This form avoids superfluous object creation when the logger is disabled
167     * for the TRACE level.
168     * </p>
169     * 
170     * @param format
171     *          the format string
172     * @param arguments
173     *          an array of arguments
174     */
175    public void trace(String format, Object... arguments) {
176        if (isTraceEnabled()) {
177            FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments);
178            logger.log(FQCN, traceCapable ? Level.TRACE : Level.DEBUG, ft.getMessage(), ft.getThrowable());
179        }
180    }
181
182    /**
183     * Log an exception (throwable) at level TRACE with an accompanying message.
184     * 
185     * @param msg
186     *          the message accompanying the exception
187     * @param t
188     *          the exception (throwable) to log
189     */
190    public void trace(String msg, Throwable t) {
191        logger.log(FQCN, traceCapable ? Level.TRACE : Level.DEBUG, msg, t);
192    }
193
194    /**
195     * Is this logger instance enabled for the DEBUG level?
196     * 
197     * @return True if this Logger is enabled for level DEBUG, false otherwise.
198     */
199    public boolean isDebugEnabled() {
200        return logger.isDebugEnabled();
201    }
202
203    /**
204     * Log a message object at level DEBUG.
205     * 
206     * @param msg
207     *          - the message object to be logged
208     */
209    public void debug(String msg) {
210        logger.log(FQCN, Level.DEBUG, msg, null);
211    }
212
213    /**
214     * Log a message at level DEBUG according to the specified format and
215     * argument.
216     * 
217     * <p>
218     * This form avoids superfluous object creation when the logger is disabled
219     * for level DEBUG.
220     * </p>
221     * 
222     * @param format
223     *          the format string
224     * @param arg
225     *          the argument
226     */
227    public void debug(String format, Object arg) {
228        if (logger.isDebugEnabled()) {
229            FormattingTuple ft = MessageFormatter.format(format, arg);
230            logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
231        }
232    }
233
234    /**
235     * Log a message at level DEBUG according to the specified format and
236     * arguments.
237     * 
238     * <p>
239     * This form avoids superfluous object creation when the logger is disabled
240     * for the DEBUG level.
241     * </p>
242     * 
243     * @param format
244     *          the format string
245     * @param arg1
246     *          the first argument
247     * @param arg2
248     *          the second argument
249     */
250    public void debug(String format, Object arg1, Object arg2) {
251        if (logger.isDebugEnabled()) {
252            FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
253            logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
254        }
255    }
256
257    /**
258     * Log a message at level DEBUG according to the specified format and
259     * arguments.
260     * 
261     * <p>
262     * This form avoids superfluous object creation when the logger is disabled
263     * for the DEBUG level.
264     * </p>
265     * 
266     * @param format
267     *          the format string
268     * @param arguments an array of arguments
269     */
270    public void debug(String format, Object... arguments) {
271        if (logger.isDebugEnabled()) {
272            FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments);
273            logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
274        }
275    }
276
277    /**
278     * Log an exception (throwable) at level DEBUG with an accompanying message.
279     * 
280     * @param msg
281     *          the message accompanying the exception
282     * @param t
283     *          the exception (throwable) to log
284     */
285    public void debug(String msg, Throwable t) {
286        logger.log(FQCN, Level.DEBUG, msg, t);
287    }
288
289    /**
290     * Is this logger instance enabled for the INFO level?
291     * 
292     * @return True if this Logger is enabled for the INFO level, false otherwise.
293     */
294    public boolean isInfoEnabled() {
295        return logger.isInfoEnabled();
296    }
297
298    /**
299     * Log a message object at the INFO level.
300     * 
301     * @param msg
302     *          - the message object to be logged
303     */
304    public void info(String msg) {
305        logger.log(FQCN, Level.INFO, msg, null);
306    }
307
308    /**
309     * Log a message at level INFO according to the specified format and argument.
310     * 
311     * <p>
312     * This form avoids superfluous object creation when the logger is disabled
313     * for the INFO level.
314     * </p>
315     * 
316     * @param format
317     *          the format string
318     * @param arg
319     *          the argument
320     */
321    public void info(String format, Object arg) {
322        if (logger.isInfoEnabled()) {
323            FormattingTuple ft = MessageFormatter.format(format, arg);
324            logger.log(FQCN, Level.INFO, ft.getMessage(), ft.getThrowable());
325        }
326    }
327
328    /**
329     * Log a message at the INFO level according to the specified format and
330     * arguments.
331     * 
332     * <p>
333     * This form avoids superfluous object creation when the logger is disabled
334     * for the INFO level.
335     * </p>
336     * 
337     * @param format
338     *          the format string
339     * @param arg1
340     *          the first argument
341     * @param arg2
342     *          the second argument
343     */
344    public void info(String format, Object arg1, Object arg2) {
345        if (logger.isInfoEnabled()) {
346            FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
347            logger.log(FQCN, Level.INFO, ft.getMessage(), ft.getThrowable());
348        }
349    }
350
351    /**
352     * Log a message at level INFO according to the specified format and
353     * arguments.
354     * 
355     * <p>
356     * This form avoids superfluous object creation when the logger is disabled
357     * for the INFO level.
358     * </p>
359     * 
360     * @param format
361     *          the format string
362     * @param argArray
363     *          an array of arguments
364     */
365    public void info(String format, Object... argArray) {
366        if (logger.isInfoEnabled()) {
367            FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
368            logger.log(FQCN, Level.INFO, ft.getMessage(), ft.getThrowable());
369        }
370    }
371
372    /**
373     * Log an exception (throwable) at the INFO level with an accompanying
374     * message.
375     * 
376     * @param msg
377     *          the message accompanying the exception
378     * @param t
379     *          the exception (throwable) to log
380     */
381    public void info(String msg, Throwable t) {
382        logger.log(FQCN, Level.INFO, msg, t);
383    }
384
385    /**
386     * Is this logger instance enabled for the WARN level?
387     * 
388     * @return True if this Logger is enabled for the WARN level, false otherwise.
389     */
390    public boolean isWarnEnabled() {
391        return logger.isEnabledFor(Level.WARN);
392    }
393
394    /**
395     * Log a message object at the WARN level.
396     * 
397     * @param msg
398     *          - the message object to be logged
399     */
400    public void warn(String msg) {
401        logger.log(FQCN, Level.WARN, msg, null);
402    }
403
404    /**
405     * Log a message at the WARN level according to the specified format and
406     * argument.
407     * 
408     * <p>
409     * This form avoids superfluous object creation when the logger is disabled
410     * for the WARN level.
411     * </p>
412     * 
413     * @param format
414     *          the format string
415     * @param arg
416     *          the argument
417     */
418    public void warn(String format, Object arg) {
419        if (logger.isEnabledFor(Level.WARN)) {
420            FormattingTuple ft = MessageFormatter.format(format, arg);
421            logger.log(FQCN, Level.WARN, ft.getMessage(), ft.getThrowable());
422        }
423    }
424
425    /**
426     * Log a message at the WARN level according to the specified format and
427     * arguments.
428     * 
429     * <p>
430     * This form avoids superfluous object creation when the logger is disabled
431     * for the WARN level.
432     * </p>
433     * 
434     * @param format
435     *          the format string
436     * @param arg1
437     *          the first argument
438     * @param arg2
439     *          the second argument
440     */
441    public void warn(String format, Object arg1, Object arg2) {
442        if (logger.isEnabledFor(Level.WARN)) {
443            FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
444            logger.log(FQCN, Level.WARN, ft.getMessage(), ft.getThrowable());
445        }
446    }
447
448    /**
449     * Log a message at level WARN according to the specified format and
450     * arguments.
451     * 
452     * <p>
453     * This form avoids superfluous object creation when the logger is disabled
454     * for the WARN level.
455     * </p>
456     * 
457     * @param format
458     *          the format string
459     * @param argArray
460     *          an array of arguments
461     */
462    public void warn(String format, Object... argArray) {
463        if (logger.isEnabledFor(Level.WARN)) {
464            FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
465            logger.log(FQCN, Level.WARN, ft.getMessage(), ft.getThrowable());
466        }
467    }
468
469    /**
470     * Log an exception (throwable) at the WARN level with an accompanying
471     * message.
472     * 
473     * @param msg
474     *          the message accompanying the exception
475     * @param t
476     *          the exception (throwable) to log
477     */
478    public void warn(String msg, Throwable t) {
479        logger.log(FQCN, Level.WARN, msg, t);
480    }
481
482    /**
483     * Is this logger instance enabled for level ERROR?
484     * 
485     * @return True if this Logger is enabled for level ERROR, false otherwise.
486     */
487    public boolean isErrorEnabled() {
488        return logger.isEnabledFor(Level.ERROR);
489    }
490
491    /**
492     * Log a message object at the ERROR level.
493     * 
494     * @param msg
495     *          - the message object to be logged
496     */
497    public void error(String msg) {
498        logger.log(FQCN, Level.ERROR, msg, null);
499    }
500
501    /**
502     * Log a message at the ERROR level according to the specified format and
503     * argument.
504     * 
505     * <p>
506     * This form avoids superfluous object creation when the logger is disabled
507     * for the ERROR level.
508     * </p>
509     * 
510     * @param format
511     *          the format string
512     * @param arg
513     *          the argument
514     */
515    public void error(String format, Object arg) {
516        if (logger.isEnabledFor(Level.ERROR)) {
517            FormattingTuple ft = MessageFormatter.format(format, arg);
518            logger.log(FQCN, Level.ERROR, ft.getMessage(), ft.getThrowable());
519        }
520    }
521
522    /**
523     * Log a message at the ERROR level according to the specified format and
524     * arguments.
525     * 
526     * <p>
527     * This form avoids superfluous object creation when the logger is disabled
528     * for the ERROR level.
529     * </p>
530     * 
531     * @param format
532     *          the format string
533     * @param arg1
534     *          the first argument
535     * @param arg2
536     *          the second argument
537     */
538    public void error(String format, Object arg1, Object arg2) {
539        if (logger.isEnabledFor(Level.ERROR)) {
540            FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
541            logger.log(FQCN, Level.ERROR, ft.getMessage(), ft.getThrowable());
542        }
543    }
544
545    /**
546     * Log a message at level ERROR according to the specified format and
547     * arguments.
548     * 
549     * <p>
550     * This form avoids superfluous object creation when the logger is disabled
551     * for the ERROR level.
552     * </p>
553     * 
554     * @param format
555     *          the format string
556     * @param argArray
557     *          an array of arguments
558     */
559    public void error(String format, Object... argArray) {
560        if (logger.isEnabledFor(Level.ERROR)) {
561            FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
562            logger.log(FQCN, Level.ERROR, ft.getMessage(), ft.getThrowable());
563        }
564    }
565
566    /**
567     * Log an exception (throwable) at the ERROR level with an accompanying
568     * message.
569     * 
570     * @param msg
571     *          the message accompanying the exception
572     * @param t
573     *          the exception (throwable) to log
574     */
575    public void error(String msg, Throwable t) {
576        logger.log(FQCN, Level.ERROR, msg, t);
577    }
578
579    public void log(Marker marker, String callerFQCN, int level, String msg, Object[] argArray, Throwable t) {
580        Level log4jLevel = toLog4jLevel(level);
581        logger.log(callerFQCN, log4jLevel, msg, t);
582    }
583
584    private Level toLog4jLevel(int level) {
585        Level log4jLevel;
586        switch (level) {
587        case LocationAwareLogger.TRACE_INT:
588            log4jLevel = traceCapable ? Level.TRACE : Level.DEBUG;
589            break;
590        case LocationAwareLogger.DEBUG_INT:
591            log4jLevel = Level.DEBUG;
592            break;
593        case LocationAwareLogger.INFO_INT:
594            log4jLevel = Level.INFO;
595            break;
596        case LocationAwareLogger.WARN_INT:
597            log4jLevel = Level.WARN;
598            break;
599        case LocationAwareLogger.ERROR_INT:
600            log4jLevel = Level.ERROR;
601            break;
602        default:
603            throw new IllegalStateException("Level number " + level + " is not recognized.");
604        }
605        return log4jLevel;
606    }
607
608    public void log(LoggingEvent event) {
609        Level log4jLevel = toLog4jLevel(event.getLevel().toInt());
610        if (!logger.isEnabledFor(log4jLevel))
611            return;
612
613        org.apache.log4j.spi.LoggingEvent log4jevent = toLog4jEvent(event, log4jLevel);
614        logger.callAppenders(log4jevent);
615
616    }
617
618    private org.apache.log4j.spi.LoggingEvent toLog4jEvent(LoggingEvent event, Level log4jLevel) {
619
620        FormattingTuple ft = MessageFormatter.format(event.getMessage(), event.getArgumentArray(), event.getThrowable());
621
622        LocationInfo locationInfo = new LocationInfo(NA_SUBST, NA_SUBST, NA_SUBST, "0");
623
624        ThrowableInformation ti = null;
625        Throwable t = ft.getThrowable();
626        if (t != null)
627            ti = new ThrowableInformation(t);
628
629        org.apache.log4j.spi.LoggingEvent log4jEvent = new org.apache.log4j.spi.LoggingEvent(FQCN, logger, event.getTimeStamp(), log4jLevel, ft.getMessage(),
630                        event.getThreadName(), ti, null, locationInfo, null);
631
632        return log4jEvent;
633    }
634
635}