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.event.SubstituteLoggingEvent;
038import org.slf4j.helpers.LegacyAbstractLogger;
039import org.slf4j.helpers.MessageFormatter;
040import org.slf4j.helpers.NormalizedParameters;
041import org.slf4j.helpers.SubstituteLogger;
042import org.slf4j.spi.LocationAwareLogger;
043import org.slf4j.spi.LoggingEventAware;
044import org.slf4j.spi.LoggingEventBuilder;
045
046/**
047 * A wrapper over {@link org.apache.log4j.Logger org.apache.log4j.Logger} in
048 * conforming to the {@link Logger} interface.
049 * 
050 * <p>
051 * Note that the logging levels mentioned in this class refer to those defined
052 * in the <a href=
053 * "http://logging.apache.org/log4j/docs/api/org/apache/log4j/Level.html">
054 * <code>org.apache.log4j.Level</code></a> class.
055 * 
056 * <p>
057 * The TRACE level was introduced in log4j version 1.2.12. In order to avoid
058 * crashing the host application, in the case the log4j version in use predates
059 * 1.2.12, the TRACE level will be mapped as DEBUG. See also
060 * <a href="http://jira.qos.ch/browse/SLF4J-59">SLF4J-59</a>.
061 * 
062 * @author Ceki G&uuml;lc&uuml;
063 */
064public final class Log4jLoggerAdapter extends LegacyAbstractLogger implements LocationAwareLogger, LoggingEventAware, Serializable {
065
066    private static final long serialVersionUID = 6182834493563598289L;
067
068    final transient org.apache.log4j.Logger logger;
069
070    /**
071     * Following the pattern discussed in pages 162 through 168 of "The complete
072     * log4j manual".
073     */
074
075    final static String FQCN_NOMINAL = org.slf4j.helpers.AbstractLogger.class.getName();
076    final static String FQCN_SUBSTITUE = FQCN_NOMINAL;
077    final static String FQCN_FLUENT = org.slf4j.spi.DefaultLoggingEventBuilder.class.getName();
078
079    // Does the log4j version in use recognize the TRACE level?
080    // The trace level was introduced in log4j 1.2.12.
081    final boolean traceCapable;
082
083    // WARN: Log4jLoggerAdapter constructor should have only package access so
084    // that only Log4jLoggerFactory be able to create one.
085    Log4jLoggerAdapter(org.apache.log4j.Logger logger) {
086        this.logger = logger;
087        this.name = logger.getName();
088        traceCapable = isTraceCapable();
089    }
090
091    private boolean isTraceCapable() {
092        try {
093            logger.isTraceEnabled();
094            return true;
095        } catch (NoSuchMethodError e) {
096            return false;
097        }
098    }
099
100    /**
101     * Is this logger instance enabled for the TRACE level?
102     * 
103     * @return True if this Logger is enabled for level TRACE, false otherwise.
104     */
105    public boolean isTraceEnabled() {
106        if (traceCapable) {
107            return logger.isTraceEnabled();
108        } else {
109            return logger.isDebugEnabled();
110        }
111    }
112
113    /**
114     * Is this logger instance enabled for the DEBUG level?
115     * 
116     * @return True if this Logger is enabled for level DEBUG, false otherwise.
117     */
118    public boolean isDebugEnabled() {
119        return logger.isDebugEnabled();
120    }
121
122    /**
123     * Is this logger instance enabled for the INFO level?
124     * 
125     * @return True if this Logger is enabled for the INFO level, false otherwise.
126     */
127    public boolean isInfoEnabled() {
128        return logger.isInfoEnabled();
129    }
130
131    /**
132     * Is this logger instance enabled for the WARN level?
133     * 
134     * @return True if this Logger is enabled for the WARN level, false otherwise.
135     */
136    public boolean isWarnEnabled() {
137        return logger.isEnabledFor(Level.WARN);
138    }
139
140    /**
141     * Is this logger instance enabled for level ERROR?
142     * 
143     * @return True if this Logger is enabled for level ERROR, false otherwise.
144     */
145    public boolean isErrorEnabled() {
146        return logger.isEnabledFor(Level.ERROR);
147    }
148
149    @Override
150    public void log(Marker marker, String callerFQCN, int level, String msg, Object[] arguments, Throwable t) {
151        Level log4jLevel = toLog4jLevel(level);
152        NormalizedParameters np = NormalizedParameters.normalize(msg, arguments, t);
153        String formattedMessage = MessageFormatter.basicArrayFormat(np.getMessage(), np.getArguments());
154        logger.log(callerFQCN, log4jLevel, formattedMessage, np.getThrowable());
155    }
156
157    @Override
158    protected void handleNormalizedLoggingCall(org.slf4j.event.Level level, Marker marker, String msg, Object[] arguments, Throwable throwable) {
159        Level log4jLevel = toLog4jLevel(level.toInt());
160        String formattedMessage = MessageFormatter.basicArrayFormat(msg, arguments);
161        logger.log(getFullyQualifiedCallerName(), log4jLevel, formattedMessage, throwable);
162    }
163
164    /**
165     * Called by {@link SubstituteLogger} or by {@link LoggingEventBuilder} instances
166     * @param event
167     */
168    public void log(LoggingEvent event) {
169        Level log4jLevel = toLog4jLevel(event.getLevel().toInt());
170        if (!logger.isEnabledFor(log4jLevel))
171            return;
172
173        org.apache.log4j.spi.LoggingEvent log4jevent = event2Log4jEvent(event, log4jLevel);
174        logger.callAppenders(log4jevent);
175
176    }
177
178    private org.apache.log4j.spi.LoggingEvent event2Log4jEvent(LoggingEvent event, Level log4jLevel) {
179
180        String formattedMessage = MessageFormatter.basicArrayFormat(event.getMessage(), event.getArgumentArray());
181
182        LocationInfo locationInfo = null;
183        String fcqn = null;
184
185        if (event instanceof SubstituteLoggingEvent) {
186            locationInfo = new LocationInfo(NA_SUBST, NA_SUBST, NA_SUBST, "0");
187            fcqn = FQCN_SUBSTITUE;
188        } else {
189            fcqn = FQCN_FLUENT;
190        }
191
192        ThrowableInformation ti = null;
193        Throwable t = event.getThrowable();
194        if (t != null)
195            ti = new ThrowableInformation(t);
196
197        org.apache.log4j.spi.LoggingEvent log4jEvent = new org.apache.log4j.spi.LoggingEvent(fcqn, logger, event.getTimeStamp(), log4jLevel, formattedMessage,
198                        event.getThreadName(), ti, null, locationInfo, null);
199
200        return log4jEvent;
201    }
202
203    private Level toLog4jLevel(int slf4jLevelInt) {
204        Level log4jLevel;
205        switch (slf4jLevelInt) {
206        case LocationAwareLogger.TRACE_INT:
207            log4jLevel = traceCapable ? Level.TRACE : Level.DEBUG;
208            break;
209        case LocationAwareLogger.DEBUG_INT:
210            log4jLevel = Level.DEBUG;
211            break;
212        case LocationAwareLogger.INFO_INT:
213            log4jLevel = Level.INFO;
214            break;
215        case LocationAwareLogger.WARN_INT:
216            log4jLevel = Level.WARN;
217            break;
218        case LocationAwareLogger.ERROR_INT:
219            log4jLevel = Level.ERROR;
220            break;
221        default:
222            throw new IllegalStateException("Level number " + slf4jLevelInt + " is not recognized.");
223        }
224        return log4jLevel;
225    }
226
227    @Override
228    protected String getFullyQualifiedCallerName() {
229        return FQCN_NOMINAL;
230    }
231
232}