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    @Override
187    public Object getAttribute(final String name) {
188        return attributes.get(name);
189    }
190
191    @Override
192    public String[] getAttributeNames() {
193        return attributes.keySet().toArray(EMPTY_ARRAY);
194    }
195
196    @Override
197    public Log getInstance(final Class<?> clazz) {
198        return getInstance(clazz.getName());
199    }
200
201    @Override
202    public Log getInstance(final String name) {
203        return adapter.getLogger(name);
204    }
205
206    /**
207     * This method is supposed to clear all loggers. In this implementation it will clear all the logger
208     * wrappers but the loggers managed by the underlying logger context will not be.
209     */
210    @Override
211    public void release() {
212        try {
213            adapter.close();
214        } catch (final IOException ignored) {
215            // Ignore
216        }
217    }
218
219    @Override
220    public void removeAttribute(final String name) {
221        attributes.remove(name);
222    }
223
224    @Override
225    public void setAttribute(final String name, final Object value) {
226        if (value != null) {
227            attributes.put(name, value);
228        } else {
229            removeAttribute(name);
230        }
231    }
232}