+ * Based on the `log4j-jcl` artifact from Apache Logging Services. + *
+ * + * @since 1.3 + */ +public final class Log4jApiLogFactory extends LogFactory { + + private static final String[] EMPTY_ARRAY = new String[0]; + /** + * Marker used by all messages coming from Apache Commons Logging. + */ + private static final Marker MARKER = MarkerManager.getMarker("COMMONS-LOGGING"); + + /** + * Caches Log instances + */ + private final LoggerAdapter+ * The SLF4J reference implementation (Logback) has a single logger context, so each call to + * {@link #getInstance(String)} + * should give the same result. + *
+ */ + private final ConcurrentMap+ * In this implementation it calls a "stop" method if the logger factory supports it. This is the case of + * Logback. + *
+ */ + @Override + public void release() { + final ILoggerFactory factory = LoggerFactory.getILoggerFactory(); + try { + factory.getClass().getMethod("stop").invoke(factory); + } catch (final ReflectiveOperationException ignored) { + } + } + + @Override + public void removeAttribute(final String name) { + attributes.remove(name); + } + + @Override + public void setAttribute(final String name, final Object value) { + if (value != null) { + attributes.put(name, value); + } else { + removeAttribute(name); + } + } + + private static class Slf4jLog implements Log { + + private final Logger logger; + + public Slf4jLog(final Logger logger) { + this.logger = logger; + } + + @Override + public void debug(Object message) { + logger.debug(MARKER, String.valueOf(message)); + } + + @Override + public void debug(Object message, Throwable t) { + logger.debug(MARKER, String.valueOf(message), t); + } + + @Override + public void error(Object message) { + logger.error(MARKER, String.valueOf(message)); + } + + @Override + public void error(Object message, Throwable t) { + logger.debug(MARKER, String.valueOf(message), t); + } + + @Override + public void fatal(Object message) { + error(message); + } + + @Override + public void fatal(Object message, Throwable t) { + error(message, t); + } + + @Override + public void info(Object message) { + logger.info(MARKER, String.valueOf(message)); + } + + @Override + public void info(Object message, Throwable t) { + logger.info(MARKER, String.valueOf(message), t); + } + + @Override + public boolean isDebugEnabled() { + return logger.isDebugEnabled(MARKER); + } + + @Override + public boolean isErrorEnabled() { + return logger.isErrorEnabled(MARKER); + } + + @Override + public boolean isFatalEnabled() { + return isErrorEnabled(); + } + + @Override + public boolean isInfoEnabled() { + return logger.isInfoEnabled(MARKER); + } + + @Override + public boolean isTraceEnabled() { + return logger.isTraceEnabled(MARKER); + } + + @Override + public boolean isWarnEnabled() { + return logger.isWarnEnabled(MARKER); + } + + @Override + public void trace(Object message) { + logger.trace(MARKER, String.valueOf(message)); + } + + @Override + public void trace(Object message, Throwable t) { + logger.trace(MARKER, String.valueOf(message), t); + } + + @Override + public void warn(Object message) { + logger.warn(MARKER, String.valueOf(message)); + } + + @Override + public void warn(Object message, Throwable t) { + logger.warn(MARKER, String.valueOf(message), t); + } + } + + private static final class Slf4jLocationAwareLog implements Log { + + private static final String FQCN = Slf4jLocationAwareLog.class.getName(); + + private final LocationAwareLogger logger; + + public Slf4jLocationAwareLog(final LocationAwareLogger logger) { + this.logger = logger; + } + + @Override + public void debug(Object message) { + log(DEBUG_INT, message, null); + } + + @Override + public void debug(Object message, Throwable t) { + log(DEBUG_INT, message, t); + } + + @Override + public void error(Object message) { + log(ERROR_INT, message, null); + } + + @Override + public void error(Object message, Throwable t) { + log(ERROR_INT, message, t); + } + + @Override + public void fatal(Object message) { + error(message); + } + + @Override + public void fatal(Object message, Throwable t) { + error(message, t); + } + + @Override + public void info(Object message) { + log(INFO_INT, message, null); + } + + @Override + public void info(Object message, Throwable t) { + log(INFO_INT, message, t); + } + + + @Override + public boolean isDebugEnabled() { + return logger.isDebugEnabled(MARKER); + } + + @Override + public boolean isErrorEnabled() { + return logger.isErrorEnabled(MARKER); + } + + @Override + public boolean isFatalEnabled() { + return isErrorEnabled(); + } + + @Override + public boolean isInfoEnabled() { + return logger.isInfoEnabled(MARKER); + } + + @Override + public boolean isTraceEnabled() { + return logger.isTraceEnabled(MARKER); + } + + @Override + public boolean isWarnEnabled() { + return logger.isWarnEnabled(MARKER); + } + + @Override + public void trace(Object message) { + log(TRACE_INT, message, null); + } + + @Override + public void trace(Object message, Throwable t) { + log(TRACE_INT, message, t); + } + + @Override + public void warn(Object message) { + log(WARN_INT, message, null); + } + + @Override + public void warn(Object message, Throwable t) { + log(WARN_INT, message, t); + } + + private void log(final int level, final Object message, final Throwable t) { + logger.log(MARKER, FQCN, level, String.valueOf(message), EMPTY_OBJECT_ARRAY, t); + } + } +} diff --git a/src/test/java/org/apache/commons/logging/log4j2/CallerInformationTestCase.java b/src/test/java/org/apache/commons/logging/log4j2/CallerInformationTestCase.java new file mode 100644 index 0000000..3ccf2df --- /dev/null +++ b/src/test/java/org/apache/commons/logging/log4j2/CallerInformationTestCase.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.logging.log4j2; + +import java.util.List; + +import junit.framework.TestCase; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.commons.logging.impl.Log4jApiLogFactory; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.MarkerManager; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.test.appender.ListAppender; +import org.apache.logging.log4j.message.ObjectMessage; +import org.apache.logging.log4j.message.SimpleMessage; + +public class CallerInformationTestCase extends TestCase { + + private static final Object OBJ = new Object(); + private static final String STRING = "String"; + private static final Throwable T = new RuntimeException(); + private static final Marker MARKER = MarkerManager.getMarker("COMMONS-LOGGING"); + + private static final Level[] levels = {Level.FATAL, Level.ERROR, Level.WARN, Level.INFO, Level.DEBUG, Level.TRACE}; + + private LogFactory factory; + private Log log; + private ListAppender appender; + + @Override + public void setUp() { + factory = LogFactory.getFactory(); + log = factory.getInstance(getClass()); + final LoggerContext context = LoggerContext.getContext(false); + final Configuration config = context.getConfiguration(); + appender = config.getAppender("LIST"); + assertNotNull("Missing Log4j 2.x appender.", appender); + } + + public void testFactoryClassName() { + assertEquals(Log4jApiLogFactory.class, factory.getClass()); + } + + public void testLocationInfo() { + appender.clear(); + // The following value must match the line number + final int currentLineNumber = 65; + log.fatal(OBJ); + log.fatal(OBJ, T); + log.error(OBJ); + log.error(OBJ, T); + log.warn(OBJ); + log.warn(OBJ, T); + log.info(OBJ); + log.info(OBJ, T); + log.debug(OBJ); + log.debug(OBJ, T); + log.trace(OBJ); + log.trace(OBJ, T); + final ObjectMessage expectedMessage = new ObjectMessage(OBJ); + final List