    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.logging.impl;
    018
    019import java.io.IOException;
    020import java.util.concurrent.ConcurrentHashMap;
    021import java.util.concurrent.ConcurrentMap;
    022
    023import org.apache.commons.logging.Log;
    024import org.apache.commons.logging.LogFactory;
    025import org.apache.logging.log4j.Level;
    026import org.apache.logging.log4j.LogManager;
    027import org.apache.logging.log4j.Marker;
    028import org.apache.logging.log4j.MarkerManager;
    029import org.apache.logging.log4j.spi.AbstractLoggerAdapter;
    030import org.apache.logging.log4j.spi.ExtendedLogger;
    031import org.apache.logging.log4j.spi.LoggerAdapter;
    032import org.apache.logging.log4j.spi.LoggerContext;
    033import org.apache.logging.log4j.util.StackLocatorUtil;
    034
    035/**
    036 * Logger factory hardcoded to send everything to Log4j API.
    037 * <p>
    038 * Based on the `log4j-jcl` artifact from Apache Logging Services.
    039 * </p>
    040 *
    041 * @since 1.3.0
    042 */
    043public final class Log4jApiLogFactory extends LogFactory {
    044
    045    private static final class Log4j2Log implements Log {
    046
    047        private static final String FQCN = Log4j2Log.class.getName();
    048
    049        private final ExtendedLogger logger;
    050
    051        public Log4j2Log(final ExtendedLogger logger) {
    052            this.logger = logger;
    053        }
    054
    055        @Override
    056        public void debug(final Object message) {
    057            logIfEnabled(Level.DEBUG, message, null);
    058        }
    059
    060        @Override
    061        public void debug(final Object message, final Throwable t) {
    062            logIfEnabled(Level.DEBUG, message, t);
    063        }
    064
    065        @Override
    066        public void error(final Object message) {
    067            logIfEnabled(Level.ERROR, message, null);
    068        }
    069
    070        @Override
    071        public void error(final Object message, final Throwable t) {
    072            logIfEnabled(Level.ERROR, message, t);
    073        }
    074
    075        @Override
    076        public void fatal(final Object message) {
    077            logIfEnabled(Level.FATAL, message, null);
    078        }
    079
    080        @Override
    081        public void fatal(final Object message, final Throwable t) {
    082            logIfEnabled(Level.FATAL, message, t);
    083        }
    084
    085        @Override
    086        public void info(final Object message) {
    087            logIfEnabled(Level.INFO, message, null);
    088        }
    089
    090        @Override
    091        public void info(final Object message, final Throwable t) {
    092            logIfEnabled(Level.INFO, message, t);
    093        }
    094
    095        @Override
    096        public boolean isDebugEnabled() {
    097            return isEnabled(Level.DEBUG);
    098        }
    099
    100        private boolean isEnabled(final Level level) {
    101            return logger.isEnabled(level, MARKER, null);
    102        }
    103
    104        @Override
    105        public boolean isErrorEnabled() {
    106            return isEnabled(Level.ERROR);
    107        }
    108
    109        @Override
    110        public boolean isFatalEnabled() {
    111            return isEnabled(Level.FATAL);
    112        }
    113
    114        @Override
    115        public boolean isInfoEnabled() {
    116            return isEnabled(Level.INFO);
    117        }
    118
    119        @Override
    120        public boolean isTraceEnabled() {
    121            return isEnabled(Level.TRACE);
    122        }
    123
    124        @Override
    125        public boolean isWarnEnabled() {
    126            return isEnabled(Level.WARN);
    127        }
    128
    129        private void logIfEnabled(final Level level, final Object message, final Throwable t) {
    130            if (message instanceof CharSequence) {
    131                logger.logIfEnabled(FQCN, level, MARKER, (CharSequence) message, t);
    132            } else {
    133                logger.logIfEnabled(FQCN, level, MARKER, message, t);
    134            }
    135        }
    136
    137        @Override
    138        public void trace(final Object message) {
    139            logIfEnabled(Level.TRACE, message, null);
    140        }
    141
    142        @Override
    143        public void trace(final Object message, final Throwable t) {
    144            logIfEnabled(Level.TRACE, message, t);
    145        }
    146
    147        @Override
    148        public void warn(final Object message) {
    149            logIfEnabled(Level.WARN, message, null);
    150        }
    151
    152        @Override
    153        public void warn(final Object message, final Throwable t) {
    154            logIfEnabled(Level.WARN, message, t);
    155        }
    156    }
    157    private static final class LogAdapter extends AbstractLoggerAdapter<Log> {
    158
    159        @Override
    160        protected LoggerContext getContext() {
    161            return getContext(LogManager.getFactory().isClassLoaderDependent() ? StackLocatorUtil.getCallerClass(
    162                    LogFactory.class) : null);
    163        }
    164
    165        @Override
    166        protected Log newLogger(final String name, final LoggerContext context) {
    167            return new Log4j2Log(context.getLogger(name));
    168        }
    169
    170    }
    171
    172    private static final String[] EMPTY_ARRAY = {};
    173
    174    /**
    175     * Marker used by all messages coming from Apache Commons Logging.
    176     */
    177    private static final Marker MARKER = MarkerManager.getMarker("COMMONS-LOGGING");
    178
    179    /**
    180     * Caches Log instances
    181     */
    182    private final LoggerAdapter<Log> adapter = new LogAdapter();
    183
    184    private final ConcurrentMap<String, Object> attributes = new ConcurrentHashMap<>();
    185
    186    /**
    187     * Constructs a new instance.
    188     */
    189    public Log4jApiLogFactory() {
    190        // empty
    191    }
    192
    193    @Override
    194    public Object getAttribute(final String name) {
    195        return attributes.get(name);
    196    }
    197
    198    @Override
    199    public String[] getAttributeNames() {
    200        return attributes.keySet().toArray(EMPTY_ARRAY);
    201    }
    202
    203    @Override
    204    public Log getInstance(final Class<?> clazz) {
    205        return getInstance(clazz.getName());
    206    }
    207
    208    @Override
    209    public Log getInstance(final String name) {
    210        return adapter.getLogger(name);
    211    }
    212
    213    /**
    214     * This method is supposed to clear all loggers. In this implementation it will clear all the logger
    215     * wrappers but the loggers managed by the underlying logger context will not be.
    216     */
    217    @Override
    218    public void release() {
    219        try {
    220            adapter.close();
    221        } catch (final IOException ignored) {
    222            // Ignore
    223        }
    224    }
    225
    226    @Override
    227    public void removeAttribute(final String name) {
    228        attributes.remove(name);
    229    }
    230
    231    @Override
    232    public void setAttribute(final String name, final Object value) {
    233        if (value != null) {
    234            attributes.put(name, value);
    235        } else {
    236            removeAttribute(name);
    237        }
    238    }
    239}