diff --git a/LICENSE.txt b/LICENSE.txt index 315bd497..1a3d0532 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2004-2017 QOS.ch +Copyright (c) 2004-2022 QOS.ch Sarl (Switzerland) All rights reserved. Permission is hereby granted, free of charge, to any person obtaining diff --git a/pom.xml b/pom.xml index 404065d3..29aac0d9 100755 --- a/pom.xml +++ b/pom.xml @@ -44,6 +44,7 @@ 1.6.0 0.8.1 + 1.2.18.0 1.2.17 1.2.3 4.13.1 @@ -74,6 +75,7 @@ slf4j-jdk14 slf4j-jdk-platform-logging slf4j-log4j12 + slf4j-reload4j slf4j-ext jcl-over-slf4j log4j-over-slf4j @@ -123,6 +125,12 @@ ${log4j.version} + + ch.qos.reload4j + reload4j + ${reload4j.version} + + ch.qos.cal10n cal10n-api diff --git a/slf4j-reload4j/LICENSE.txt b/slf4j-reload4j/LICENSE.txt new file mode 100644 index 00000000..1a3d0532 --- /dev/null +++ b/slf4j-reload4j/LICENSE.txt @@ -0,0 +1,24 @@ +Copyright (c) 2004-2022 QOS.ch Sarl (Switzerland) +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + diff --git a/slf4j-reload4j/pom.xml b/slf4j-reload4j/pom.xml new file mode 100644 index 00000000..b03a13c8 --- /dev/null +++ b/slf4j-reload4j/pom.xml @@ -0,0 +1,67 @@ + + + + 4.0.0 + + + org.slf4j + slf4j-parent + 2.0.0-alpha6-SNAPSHOT + + + slf4j-reload4j + + + jar + SLF4J Reload4j Binding + SLF4J Reload4j Binding + http://reload4j.qos.ch + + + + + org.slf4j + slf4j-api + + + + ch.qos.reload4j + reload4j + + + + org.slf4j + slf4j-api + test-jar + ${project.version} + test + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.19.1 + + 1 + false + plain + false + + **/AllTest.java + **/PackageTest.java + + -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8001 + + + + + + + + diff --git a/slf4j-reload4j/src/main/java/org/slf4j/reload4j/Reload4jLoggerAdapter.java b/slf4j-reload4j/src/main/java/org/slf4j/reload4j/Reload4jLoggerAdapter.java new file mode 100644 index 00000000..e4f3e117 --- /dev/null +++ b/slf4j-reload4j/src/main/java/org/slf4j/reload4j/Reload4jLoggerAdapter.java @@ -0,0 +1,232 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.reload4j; + +import static org.slf4j.event.EventConstants.NA_SUBST; + +import java.io.Serializable; + +import org.apache.log4j.Level; +import org.apache.log4j.spi.LocationInfo; +import org.apache.log4j.spi.ThrowableInformation; +import org.slf4j.Logger; +import org.slf4j.Marker; +import org.slf4j.event.LoggingEvent; +import org.slf4j.event.SubstituteLoggingEvent; +import org.slf4j.helpers.LegacyAbstractLogger; +import org.slf4j.helpers.MessageFormatter; +import org.slf4j.helpers.NormalizedParameters; +import org.slf4j.helpers.SubstituteLogger; +import org.slf4j.spi.LocationAwareLogger; +import org.slf4j.spi.LoggingEventAware; +import org.slf4j.spi.LoggingEventBuilder; + +/** + * A wrapper over {@link org.apache.log4j.Logger org.apache.log4j.Logger} in + * conforming to the {@link Logger} interface. + * + *

+ * Note that the logging levels mentioned in this class refer to those defined + * in the + * org.apache.log4j.Level class. + * + *

+ * The TRACE level was introduced in log4j version 1.2.12. In order to avoid + * crashing the host application, in the case the log4j version in use predates + * 1.2.12, the TRACE level will be mapped as DEBUG. See also + * SLF4J-59. + * + * @author Ceki Gülcü + */ +public final class Reload4jLoggerAdapter extends LegacyAbstractLogger implements LocationAwareLogger, LoggingEventAware, Serializable { + + private static final long serialVersionUID = 6182834493563598289L; + + final transient org.apache.log4j.Logger logger; + + /** + * Following the pattern discussed in pages 162 through 168 of "The complete + * log4j manual". + */ + + final static String FQCN_NOMINAL = org.slf4j.helpers.AbstractLogger.class.getName(); + final static String FQCN_SUBSTITUE = FQCN_NOMINAL; + final static String FQCN_FLUENT = org.slf4j.spi.DefaultLoggingEventBuilder.class.getName(); + + // Does the log4j version in use recognize the TRACE level? + // The trace level was introduced in log4j 1.2.12. + final boolean traceCapable; + + // WARN: Log4jLoggerAdapter constructor should have only package access so + // that only Log4jLoggerFactory be able to create one. + Reload4jLoggerAdapter(org.apache.log4j.Logger logger) { + this.logger = logger; + this.name = logger.getName(); + traceCapable = isTraceCapable(); + } + + private boolean isTraceCapable() { + try { + logger.isTraceEnabled(); + return true; + } catch (NoSuchMethodError e) { + return false; + } + } + + /** + * Is this logger instance enabled for the TRACE level? + * + * @return True if this Logger is enabled for level TRACE, false otherwise. + */ + public boolean isTraceEnabled() { + if (traceCapable) { + return logger.isTraceEnabled(); + } else { + return logger.isDebugEnabled(); + } + } + + /** + * Is this logger instance enabled for the DEBUG level? + * + * @return True if this Logger is enabled for level DEBUG, false otherwise. + */ + public boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + + /** + * Is this logger instance enabled for the INFO level? + * + * @return True if this Logger is enabled for the INFO level, false otherwise. + */ + public boolean isInfoEnabled() { + return logger.isInfoEnabled(); + } + + /** + * Is this logger instance enabled for the WARN level? + * + * @return True if this Logger is enabled for the WARN level, false otherwise. + */ + public boolean isWarnEnabled() { + return logger.isEnabledFor(Level.WARN); + } + + /** + * Is this logger instance enabled for level ERROR? + * + * @return True if this Logger is enabled for level ERROR, false otherwise. + */ + public boolean isErrorEnabled() { + return logger.isEnabledFor(Level.ERROR); + } + + @Override + public void log(Marker marker, String callerFQCN, int level, String msg, Object[] arguments, Throwable t) { + Level log4jLevel = toLog4jLevel(level); + NormalizedParameters np = NormalizedParameters.normalize(msg, arguments, t); + String formattedMessage = MessageFormatter.basicArrayFormat(np.getMessage(), np.getArguments()); + logger.log(callerFQCN, log4jLevel, formattedMessage, np.getThrowable()); + } + + @Override + protected void handleNormalizedLoggingCall(org.slf4j.event.Level level, Marker marker, String msg, Object[] arguments, Throwable throwable) { + Level log4jLevel = toLog4jLevel(level.toInt()); + String formattedMessage = MessageFormatter.basicArrayFormat(msg, arguments); + logger.log(getFullyQualifiedCallerName(), log4jLevel, formattedMessage, throwable); + } + + /** + * Called by {@link SubstituteLogger} or by {@link LoggingEventBuilder} instances + * @param event + */ + public void log(LoggingEvent event) { + Level log4jLevel = toLog4jLevel(event.getLevel().toInt()); + if (!logger.isEnabledFor(log4jLevel)) + return; + + org.apache.log4j.spi.LoggingEvent log4jevent = event2Log4jEvent(event, log4jLevel); + logger.callAppenders(log4jevent); + + } + + private org.apache.log4j.spi.LoggingEvent event2Log4jEvent(LoggingEvent event, Level log4jLevel) { + + String formattedMessage = MessageFormatter.basicArrayFormat(event.getMessage(), event.getArgumentArray()); + + LocationInfo locationInfo = null; + String fcqn = null; + + if (event instanceof SubstituteLoggingEvent) { + locationInfo = new LocationInfo(NA_SUBST, NA_SUBST, NA_SUBST, "0"); + fcqn = FQCN_SUBSTITUE; + } else { + fcqn = FQCN_FLUENT; + } + + ThrowableInformation ti = null; + Throwable t = event.getThrowable(); + if (t != null) + ti = new ThrowableInformation(t); + + org.apache.log4j.spi.LoggingEvent log4jEvent = new org.apache.log4j.spi.LoggingEvent(fcqn, logger, event.getTimeStamp(), log4jLevel, formattedMessage, + event.getThreadName(), ti, null, locationInfo, null); + + return log4jEvent; + } + + private Level toLog4jLevel(int slf4jLevelInt) { + Level log4jLevel; + switch (slf4jLevelInt) { + case LocationAwareLogger.TRACE_INT: + log4jLevel = traceCapable ? Level.TRACE : Level.DEBUG; + break; + case LocationAwareLogger.DEBUG_INT: + log4jLevel = Level.DEBUG; + break; + case LocationAwareLogger.INFO_INT: + log4jLevel = Level.INFO; + break; + case LocationAwareLogger.WARN_INT: + log4jLevel = Level.WARN; + break; + case LocationAwareLogger.ERROR_INT: + log4jLevel = Level.ERROR; + break; + default: + throw new IllegalStateException("Level number " + slf4jLevelInt + " is not recognized."); + } + return log4jLevel; + } + + @Override + protected String getFullyQualifiedCallerName() { + return FQCN_NOMINAL; + } + +} diff --git a/slf4j-reload4j/src/main/java/org/slf4j/reload4j/Reload4jLoggerFactory.java b/slf4j-reload4j/src/main/java/org/slf4j/reload4j/Reload4jLoggerFactory.java new file mode 100644 index 00000000..4c4aadeb --- /dev/null +++ b/slf4j-reload4j/src/main/java/org/slf4j/reload4j/Reload4jLoggerFactory.java @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.reload4j; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.apache.log4j.LogManager; +import org.slf4j.ILoggerFactory; +import org.slf4j.Logger; +import org.slf4j.helpers.Util; + +/** + * Log4jLoggerFactory is an implementation of {@link ILoggerFactory} returning + * the appropriate named {@link Reload4jLoggerAdapter} instance. + * + * @author Ceki Gülcü + */ +public class Reload4jLoggerFactory implements ILoggerFactory { + + private static final String LOG4J_DELEGATION_LOOP_URL = "http://www.slf4j.org/codes.html#log4jDelegationLoop"; + + // check for delegation loops + static { + try { + Class.forName("org.apache.log4j.Log4jLoggerFactory"); + String part1 = "Detected both log4j-over-slf4j.jar AND bound slf4j-log4j12.jar on the class path, preempting StackOverflowError. "; + String part2 = "See also " + LOG4J_DELEGATION_LOOP_URL + " for more details."; + + Util.report(part1); + Util.report(part2); + throw new IllegalStateException(part1 + part2); + } catch (ClassNotFoundException e) { + // this is the good case + } + } + + // key: name (String), value: a Log4jLoggerAdapter; + ConcurrentMap loggerMap; + + public Reload4jLoggerFactory() { + loggerMap = new ConcurrentHashMap<>(); + // force log4j to initialize + org.apache.log4j.LogManager.getRootLogger(); + } + + /* + * (non-Javadoc) + * + * @see org.slf4j.ILoggerFactory#getLogger(java.lang.String) + */ + public Logger getLogger(String name) { + Logger slf4jLogger = loggerMap.get(name); + if (slf4jLogger != null) { + return slf4jLogger; + } else { + org.apache.log4j.Logger log4jLogger; + if (name.equalsIgnoreCase(Logger.ROOT_LOGGER_NAME)) + log4jLogger = LogManager.getRootLogger(); + else + log4jLogger = LogManager.getLogger(name); + + Logger newInstance = new Reload4jLoggerAdapter(log4jLogger); + Logger oldInstance = loggerMap.putIfAbsent(name, newInstance); + return oldInstance == null ? newInstance : oldInstance; + } + } +} diff --git a/slf4j-reload4j/src/main/java/org/slf4j/reload4j/Reload4jMDCAdapter.java b/slf4j-reload4j/src/main/java/org/slf4j/reload4j/Reload4jMDCAdapter.java new file mode 100644 index 00000000..f9e40372 --- /dev/null +++ b/slf4j-reload4j/src/main/java/org/slf4j/reload4j/Reload4jMDCAdapter.java @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2004-2022 QOS.ch Sarl (Switzerland) + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.reload4j; + +import java.util.Deque; +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.helpers.ThreadLocalMapOfStacks; +import org.slf4j.spi.MDCAdapter; + +public class Reload4jMDCAdapter implements MDCAdapter { + + private final ThreadLocalMapOfStacks threadLocalMapOfDeques = new ThreadLocalMapOfStacks(); + + @Override + public void clear() { + @SuppressWarnings("rawtypes") + Map map = org.apache.log4j.MDC.getContext(); + if (map != null) { + map.clear(); + } + } + + @Override + public String get(String key) { + return (String) org.apache.log4j.MDC.get(key); + } + + /** + * Put a context value (the val parameter) as identified with + * the key parameter into the current thread's context map. The + * key parameter cannot be null. Log4j does not + * support null for the val parameter. + * + *

+ * This method delegates all work to log4j's MDC. + * + * @throws IllegalArgumentException + * in case the "key" or "val" parameter is null + */ + @Override + public void put(String key, String val) { + org.apache.log4j.MDC.put(key, val); + } + + @Override + public void remove(String key) { + org.apache.log4j.MDC.remove(key); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Map getCopyOfContextMap() { + Map old = org.apache.log4j.MDC.getContext(); + if (old != null) { + return new HashMap(old); + } else { + return null; + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public void setContextMap(Map contextMap) { + Map old = org.apache.log4j.MDC.getContext(); + + // we must cater for the case where the contextMap argument is null + if (contextMap == null) { + if (old != null) { + old.clear(); + } + return; + } + + if (old == null) { + for (Map.Entry mapEntry : contextMap.entrySet()) { + org.apache.log4j.MDC.put(mapEntry.getKey(), mapEntry); + } + } else { + old.clear(); + old.putAll(contextMap); + } + } + + @Override + public void pushByKey(String key, String value) { + threadLocalMapOfDeques.pushByKey(key, value); + } + + @Override + public String popByKey(String key) { + return threadLocalMapOfDeques.popByKey(key); + } + + @Override + public Deque getCopyOfDequeByKey(String key) { + return threadLocalMapOfDeques.getCopyOfDequeByKey(key); + } + + @Override + public void clearDequeByKey(String key) { + threadLocalMapOfDeques.clearDequeByKey(key); + } +} diff --git a/slf4j-reload4j/src/main/java/org/slf4j/reload4j/Reload4jServiceProvider.java b/slf4j-reload4j/src/main/java/org/slf4j/reload4j/Reload4jServiceProvider.java new file mode 100644 index 00000000..4625b09c --- /dev/null +++ b/slf4j-reload4j/src/main/java/org/slf4j/reload4j/Reload4jServiceProvider.java @@ -0,0 +1,61 @@ +package org.slf4j.reload4j; + +import org.apache.log4j.Level; +import org.slf4j.ILoggerFactory; +import org.slf4j.IMarkerFactory; +import org.slf4j.helpers.BasicMarkerFactory; +import org.slf4j.helpers.Util; +import org.slf4j.spi.MDCAdapter; +import org.slf4j.spi.SLF4JServiceProvider; + +public class Reload4jServiceProvider implements SLF4JServiceProvider { + + /** + * Declare the version of the SLF4J API this implementation is compiled against. + * The value of this field is modified with each major release. + */ + // to avoid constant folding by the compiler, this field must *not* be final + public static String REQUESTED_API_VERSION = "2.0.99"; // !final + + private ILoggerFactory loggerFactory; + private IMarkerFactory markerFactory; + private MDCAdapter mdcAdapter; + + public Reload4jServiceProvider() { + try { + @SuppressWarnings("unused") + Level level = Level.TRACE; + } catch (NoSuchFieldError nsfe) { + Util.report("This version of SLF4J requires log4j version 1.2.12 or later. See also http://www.slf4j.org/codes.html#log4j_version"); + } + } + + @Override + public void initialize() { + loggerFactory = new Reload4jLoggerFactory(); + markerFactory = new BasicMarkerFactory(); + mdcAdapter = new Reload4jMDCAdapter(); + } + + @Override + public ILoggerFactory getLoggerFactory() { + return loggerFactory; + } + + + @Override + public IMarkerFactory getMarkerFactory() { + return markerFactory; + } + + + @Override + public MDCAdapter getMDCAdapter() { + return mdcAdapter; + } + + @Override + public String getRequestedApiVersion() { + return REQUESTED_API_VERSION; + } +} diff --git a/slf4j-reload4j/src/main/resources/META-INF/MANIFEST.MF b/slf4j-reload4j/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 00000000..980e84e6 --- /dev/null +++ b/slf4j-reload4j/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,12 @@ +Implementation-Title: slf4j-reload4j +Bundle-ManifestVersion: 2 +Bundle-SymbolicName: slf4j.reload4j +Bundle-Name: slf4j-reload4j +Bundle-Vendor: SLF4J.ORG +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Export-Package: org.slf4j.reload4j;version=${parsedVersion.osgiVersion} +Import-Package: org.slf4j;version=${parsedVersion.osgiVersion}, + org.slf4j.spi;version=${parsedVersion.osgiVersion}, + org.slf4j.helpers;version=${parsedVersion.osgiVersion}, + org.slf4j.event;version=${parsedVersion.osgiVersion}, + org.apache.log4j diff --git a/slf4j-reload4j/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider b/slf4j-reload4j/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider new file mode 100644 index 00000000..b81f3e12 --- /dev/null +++ b/slf4j-reload4j/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider @@ -0,0 +1 @@ +org.slf4j.reload4j.Reload4jServiceProvider diff --git a/slf4j-reload4j/src/test/java/org/slf4j/reload4j/InvocationTest.java b/slf4j-reload4j/src/test/java/org/slf4j/reload4j/InvocationTest.java new file mode 100644 index 00000000..12d6db19 --- /dev/null +++ b/slf4j-reload4j/src/test/java/org/slf4j/reload4j/InvocationTest.java @@ -0,0 +1,209 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.reload4j; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.spi.LoggingEvent; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; + +/** + * Test whether invoking the SLF4J API causes problems or not. + * + * @author Ceki Gulcu + * + */ +public class InvocationTest { + + ListAppender listAppender = new ListAppender(); + org.apache.log4j.Logger root; + + @Before + public void setUp() throws Exception { + root = org.apache.log4j.Logger.getRootLogger(); + root.addAppender(listAppender); + } + + @After + public void tearDown() throws Exception { + root.getLoggerRepository().resetConfiguration(); + } + + @Test + public void test1() { + + Logger logger = LoggerFactory.getLogger("test1"); + logger.debug("Hello world."); + assertEquals(1, listAppender.list.size()); + } + + @Test + public void test2() { + Integer i1 = Integer.valueOf(1); + Integer i2 = Integer.valueOf(2); + Integer i3 = Integer.valueOf(3); + Exception e = new Exception("This is a test exception."); + Logger logger = LoggerFactory.getLogger("test2"); + + logger.trace("Hello trace."); + + logger.debug("Hello world 1."); + logger.debug("Hello world {}", i1); + logger.debug("val={} val={}", i1, i2); + logger.debug("val={} val={} val={}", new Object[] { i1, i2, i3 }); + + logger.debug("Hello world 2", e); + logger.info("Hello world 2."); + + logger.warn("Hello world 3."); + logger.warn("Hello world 3", e); + + logger.error("Hello world 4."); + logger.error("Hello world {}", Integer.valueOf(3)); + logger.error("Hello world 4.", e); + assertEquals(11, listAppender.list.size()); + } + + @Test + public void testNull() { + Logger logger = LoggerFactory.getLogger("testNull"); + logger.trace(null); + logger.debug(null); + logger.info(null); + logger.warn(null); + logger.error(null); + + Exception e = new Exception("This is a test exception."); + logger.debug(null, e); + logger.info(null, e); + logger.warn(null, e); + logger.error(null, e); + assertEquals(8, listAppender.list.size()); + } + + // http://jira.qos.ch/browse/SLF4J-69 + // formerly http://bugzilla.slf4j.org/show_bug.cgi?id=78 + @Test + public void testNullParameter_BUG78() { + Logger logger = LoggerFactory.getLogger("testNullParameter_BUG78"); + String[] parameters = null; + String msg = "hello {}"; + + logger.debug(msg, (Object[]) parameters); + + assertEquals(1, listAppender.list.size()); + LoggingEvent e = (LoggingEvent) listAppender.list.get(0); + assertEquals(msg, e.getMessage()); + } + + @Test + public void testMarker() { + Logger logger = LoggerFactory.getLogger("testMarker"); + Marker blue = MarkerFactory.getMarker("BLUE"); + logger.trace(blue, "hello"); + logger.debug(blue, "hello"); + logger.info(blue, "hello"); + logger.warn(blue, "hello"); + logger.error(blue, "hello"); + + logger.debug(blue, "hello {}", "world"); + logger.info(blue, "hello {}", "world"); + logger.warn(blue, "hello {}", "world"); + logger.error(blue, "hello {}", "world"); + + logger.debug(blue, "hello {} and {} ", "world", "universe"); + logger.info(blue, "hello {} and {} ", "world", "universe"); + logger.warn(blue, "hello {} and {} ", "world", "universe"); + logger.error(blue, "hello {} and {} ", "world", "universe"); + assertEquals(12, listAppender.list.size()); + } + + @Test + public void testMDC() { + MDC.put("k", "v"); + assertNotNull(MDC.get("k")); + assertEquals("v", MDC.get("k")); + + MDC.remove("k"); + assertNull(MDC.get("k")); + + MDC.put("k1", "v1"); + assertEquals("v1", MDC.get("k1")); + MDC.clear(); + assertNull(MDC.get("k1")); + + try { + MDC.put(null, "x"); + fail("null keys are invalid"); + } catch (IllegalArgumentException e) { + } + } + + @Test + public void testMDCContextMapValues() { + Map map = new HashMap<>(); + map.put("ka", "va"); + map.put("kb", "vb"); + + MDC.put("k", "v"); + assertEquals("v", MDC.get("k")); + MDC.setContextMap(map); + assertNull(MDC.get("k")); + assertEquals("va", MDC.get("ka")); + assertEquals("vb", MDC.get("kb")); + } + + @Test + public void testCallerInfo() { + Logger logger = LoggerFactory.getLogger("testMarker"); + listAppender.extractLocationInfo = true; + logger.debug("hello"); + LoggingEvent event = listAppender.list.get(0); + assertEquals(this.getClass().getName(), event.getLocationInformation().getClassName()); + } + + @Test + public void testCallerInfoWithFluentAPI() { + Logger logger = LoggerFactory.getLogger("testMarker"); + listAppender.extractLocationInfo = true; + logger.atDebug().log("hello"); + LoggingEvent event = listAppender.list.get(0); + assertEquals(this.getClass().getName(), event.getLocationInformation().getClassName()); + } + +} diff --git a/slf4j-reload4j/src/test/java/org/slf4j/reload4j/ListAppender.java b/slf4j-reload4j/src/test/java/org/slf4j/reload4j/ListAppender.java new file mode 100644 index 00000000..92cd9d32 --- /dev/null +++ b/slf4j-reload4j/src/test/java/org/slf4j/reload4j/ListAppender.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.reload4j; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.AppenderSkeleton; +import org.apache.log4j.spi.LoggingEvent; + +public class ListAppender extends AppenderSkeleton { + + public List list = new ArrayList<>(); + + public boolean extractLocationInfo = false; + + protected void append(LoggingEvent event) { + list.add(event); + if (extractLocationInfo) { + event.getLocationInformation(); + } + } + + public void close() { + } + + public boolean requiresLayout() { + return false; + } + +} diff --git a/slf4j-reload4j/src/test/java/org/slf4j/reload4j/Log4j12MultithreadedInitializationTest.java b/slf4j-reload4j/src/test/java/org/slf4j/reload4j/Log4j12MultithreadedInitializationTest.java new file mode 100644 index 00000000..7d0e3751 --- /dev/null +++ b/slf4j-reload4j/src/test/java/org/slf4j/reload4j/Log4j12MultithreadedInitializationTest.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.reload4j; + +import static org.junit.Assert.assertNotNull; + +import java.util.List; + +import org.apache.log4j.LogManager; +import org.apache.log4j.spi.LoggingEvent; +import org.junit.After; +import org.junit.Before; +import org.slf4j.reload4j.testHarness.RecursiveAppender; + +public class Log4j12MultithreadedInitializationTest extends org.slf4j.testHarness.MultithreadedInitializationTest { + static int NUM_LINES_BY_RECURSIVE_APPENDER = 3; + + // value of LogManager.DEFAULT_CONFIGURATION_KEY; + static String CONFIG_FILE_KEY = "log4j.configuration"; + final String loggerName = this.getClass().getName(); + + @Before + public void setup() { + System.setProperty(CONFIG_FILE_KEY, "recursiveInitWithActivationDelay.properties"); + System.out.println("THREAD_COUNT=" + THREAD_COUNT); + } + + @After + public void tearDown() throws Exception { + System.clearProperty(CONFIG_FILE_KEY); + } + + protected long getRecordedEventCount() { + List eventList = getRecordedEvents(); + assertNotNull(eventList); + return eventList.size(); + } + + protected int extraLogEvents() { + return NUM_LINES_BY_RECURSIVE_APPENDER; + } + + private List getRecordedEvents() { + org.apache.log4j.Logger root = LogManager.getRootLogger(); + RecursiveAppender ra = (RecursiveAppender) root.getAppender("RECURSIVE"); + assertNotNull(ra); + return ra.events; + } + +} diff --git a/slf4j-reload4j/src/test/java/org/slf4j/reload4j/Log4jMDCAdapterTest.java b/slf4j-reload4j/src/test/java/org/slf4j/reload4j/Log4jMDCAdapterTest.java new file mode 100644 index 00000000..f6008176 --- /dev/null +++ b/slf4j-reload4j/src/test/java/org/slf4j/reload4j/Log4jMDCAdapterTest.java @@ -0,0 +1,28 @@ +package org.slf4j.reload4j; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.slf4j.helpers.MDCAdapterTestBase; +import org.slf4j.reload4j.Reload4jMDCAdapter; +import org.slf4j.spi.MDCAdapter; + +public class Log4jMDCAdapterTest extends MDCAdapterTestBase { + + protected MDCAdapter instantiateMDC() { + return new Reload4jMDCAdapter(); + } + + + @Test + public void testClearingMDC() { + mdc.put("testKey", "testValue"); + assertFalse(mdc.getCopyOfContextMap().isEmpty()); + mdc.clear(); + assertTrue(mdc.getCopyOfContextMap().isEmpty()); + } + + + +} diff --git a/slf4j-reload4j/src/test/java/org/slf4j/reload4j/RecursiveInitializationTest.java b/slf4j-reload4j/src/test/java/org/slf4j/reload4j/RecursiveInitializationTest.java new file mode 100644 index 00000000..ff68f2da --- /dev/null +++ b/slf4j-reload4j/src/test/java/org/slf4j/reload4j/RecursiveInitializationTest.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.reload4j; + +import java.util.Random; + +import org.junit.After; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RecursiveInitializationTest { + + // value of LogManager.DEFAULT_CONFIGURATION_KEY; + static String CONFIG_FILE_KEY = "log4j.configuration"; + + int diff = new Random().nextInt(10000); + String loggerName = "org.slf4j.impl.RecursiveInitializationTest"; + + @After + public void tearDown() throws Exception { + System.clearProperty(CONFIG_FILE_KEY); + } + + @Test + public void loggingDuringInitialization() { + System.setProperty(CONFIG_FILE_KEY, "recursiveInit.properties"); + Logger logger = LoggerFactory.getLogger(loggerName + ".loggingDuringInitialization-" + diff); + logger.info("hello"); + } + +} diff --git a/slf4j-reload4j/src/test/java/org/slf4j/reload4j/testHarness/RecursiveAppender.java b/slf4j-reload4j/src/test/java/org/slf4j/reload4j/testHarness/RecursiveAppender.java new file mode 100644 index 00000000..3857ba32 --- /dev/null +++ b/slf4j-reload4j/src/test/java/org/slf4j/reload4j/testHarness/RecursiveAppender.java @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.reload4j.testHarness; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import org.apache.log4j.AppenderSkeleton; +import org.apache.log4j.spi.LoggingEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RecursiveAppender extends AppenderSkeleton { + + int diff = new Random().nextInt(); + int activationDelay = 0; + String loggerName = "org.slf4j.impl.RecursiveAppender" + diff; + + public List events = new ArrayList<>(); + + public RecursiveAppender() { + System.out.println("XXXXXXX entering RecursiveAppender constructor"); + Logger logger = LoggerFactory.getLogger(loggerName); + logger.info("Calling a logger in the constructor"); + System.out.println("exiting RecursiveAppender constructor"); + } + + protected void append(LoggingEvent e) { + events.add(e); + } + + public void close() { + } + + public boolean requiresLayout() { + return false; + } + + @Override + public void activateOptions() { + System.out.println("entering RecursiveAppender.activateOptions"); + if (activationDelay > 0) { + Logger logger = LoggerFactory.getLogger(loggerName); + logger.info("About to wait {} millis", activationDelay); + try { + Thread.sleep(activationDelay); + } catch (InterruptedException e) { + e.printStackTrace(); + } + logger.info("Done waiting {} millis", activationDelay); + } + super.activateOptions(); + + System.out.println("exiting RecursiveAppender.activateOptions"); + } + + public int getActivationDelay() { + return activationDelay; + } + + public void setActivationDelay(int activationDelay) { + this.activationDelay = activationDelay; + } + +} diff --git a/slf4j-reload4j/src/test/resources/recursiveInit.properties b/slf4j-reload4j/src/test/resources/recursiveInit.properties new file mode 100644 index 00000000..a62a15e2 --- /dev/null +++ b/slf4j-reload4j/src/test/resources/recursiveInit.properties @@ -0,0 +1,9 @@ +log4j.debug=true +log4j.rootLogger=DEBUG, RECURSIVE + +log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender +log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout +log4j.appender.CONSOLE.layout.ConversionPattern=[%t] %c - %m%n + +log4j.appender.RECURSIVE=org.slf4j.reload4j.testHarness.RecursiveAppender + \ No newline at end of file diff --git a/slf4j-reload4j/src/test/resources/recursiveInitWithActivationDelay.properties b/slf4j-reload4j/src/test/resources/recursiveInitWithActivationDelay.properties new file mode 100644 index 00000000..957894dd --- /dev/null +++ b/slf4j-reload4j/src/test/resources/recursiveInitWithActivationDelay.properties @@ -0,0 +1,9 @@ +log4j.debug=true +log4j.rootLogger=DEBUG, CONSOLE, RECURSIVE + +log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender +log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout +log4j.appender.CONSOLE.layout.ConversionPattern=CON [%t] %c - %m%n + +log4j.appender.RECURSIVE=org.slf4j.reload4j.testHarness.RecursiveAppender +log4j.appender.RECURSIVE.activationDelay=10