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}