Merge remote-tracking branch 'origin' into slf4j-344-logServiceReader

Conflicts:
	osgi-over-slf4j/pom.xml
This commit is contained in:
Matt Bishop 2016-01-23 09:40:56 -08:00
commit 9bea50f144
24 changed files with 426 additions and 40 deletions

View File

@ -17,7 +17,6 @@
package org.apache.log4j; package org.apache.log4j;
import org.apache.log4j.spi.LoggerFactory; import org.apache.log4j.spi.LoggerFactory;
import org.slf4j.helpers.Util;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@ -36,23 +35,6 @@ class Log4jLoggerFactory {
// String, Logger // String, Logger
private static ConcurrentMap<String, Logger> log4jLoggers = new ConcurrentHashMap<String, Logger>(); private static ConcurrentMap<String, Logger> log4jLoggers = new ConcurrentHashMap<String, Logger>();
private static final String LOG4J_DELEGATION_LOOP_URL = "http://www.slf4j.org/codes.html#log4jDelegationLoop";
// check for delegation loops
static {
try {
Class.forName("org.slf4j.impl.Log4jLoggerFactory");
String part1 = "Detected both log4j-over-slf4j.jar AND 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
}
}
public static Logger getLogger(String name) { public static Logger getLogger(String name) {
org.apache.log4j.Logger instance = log4jLoggers.get(name); org.apache.log4j.Logger instance = log4jLoggers.get(name);
if (instance != null) { if (instance != null) {

View File

@ -0,0 +1,8 @@
Implementation-Title: osgi-over-slf4j
Bundle-ManifestVersion: 2
Bundle-SymbolicName: org.slf4j.osgi-over-slf4j
Bundle-Name: OSGi LogService implemented over SLF4J
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Bundle-Activator: org.slf4j.osgi.logservice.impl.Activator
Bundle-Category: osgi
Import-Package: org.osgi.framework;version="[1.5,2)",org.osgi.service.log;version="[1.3,2)",org.slf4j;version=${parsedVersion.osgiVersion}

View File

@ -44,6 +44,15 @@ public class StaticMDCBinder {
private StaticMDCBinder() { private StaticMDCBinder() {
} }
/**
* Return the singleton of this class.
*
* @return the StaticMDCBinder singleton
* @since 1.7.14
*/
public static final StaticMDCBinder getSingleton() {
return SINGLETON;
}
/** /**
* Currently this method always returns an instance of * Currently this method always returns an instance of
* {@link NOPMDCAdapter}. * {@link NOPMDCAdapter}.

View File

@ -49,6 +49,16 @@ public class StaticMarkerBinder implements MarkerFactoryBinder {
private StaticMarkerBinder() { private StaticMarkerBinder() {
} }
/**
* Return the singleton of this class.
*
* @return the StaticMarkerBinder singleton
* @since 1.7.14
*/
public static StaticMarkerBinder getSingleton() {
return SINGLETON;
}
/** /**
* Currently this method always returns an instance of * Currently this method always returns an instance of
* {@link BasicMarkerFactory}. * {@link BasicMarkerFactory}.

View File

@ -84,6 +84,8 @@ public final class LoggerFactory {
// Support for detecting mismatched logger names. // Support for detecting mismatched logger names.
static final String DETECT_LOGGER_NAME_MISMATCH_PROPERTY = "slf4j.detectLoggerNameMismatch"; static final String DETECT_LOGGER_NAME_MISMATCH_PROPERTY = "slf4j.detectLoggerNameMismatch";
static final String JAVA_VENDOR_PROPERTY = "java.vendor.url";
static boolean DETECT_LOGGER_NAME_MISMATCH = Util.safeGetBooleanSystemProperty(DETECT_LOGGER_NAME_MISMATCH_PROPERTY); static boolean DETECT_LOGGER_NAME_MISMATCH = Util.safeGetBooleanSystemProperty(DETECT_LOGGER_NAME_MISMATCH_PROPERTY);
/** /**
@ -254,6 +256,11 @@ public final class LoggerFactory {
* *
*/ */
private static void reportMultipleBindingAmbiguity(Set<URL> staticLoggerBinderPathSet) { private static void reportMultipleBindingAmbiguity(Set<URL> staticLoggerBinderPathSet) {
if(isAndroid()) {
// skip check under android, see also http://jira.qos.ch/browse/SLF4J-328
return;
}
if (isAmbiguousStaticLoggerBinderPathSet(staticLoggerBinderPathSet)) { if (isAmbiguousStaticLoggerBinderPathSet(staticLoggerBinderPathSet)) {
Util.report("Class path contains multiple SLF4J bindings."); Util.report("Class path contains multiple SLF4J bindings.");
for (URL path : staticLoggerBinderPathSet) { for (URL path : staticLoggerBinderPathSet) {
@ -263,6 +270,13 @@ public final class LoggerFactory {
} }
} }
private static boolean isAndroid() {
String vendor = Util.safeGetSystemProperty(JAVA_VENDOR_PROPERTY);
if(vendor == null)
return false;
return vendor.toLowerCase().contains("android");
}
private static void reportActualBinding(Set<URL> staticLoggerBinderPathSet) { private static void reportActualBinding(Set<URL> staticLoggerBinderPathSet) {
if (isAmbiguousStaticLoggerBinderPathSet(staticLoggerBinderPathSet)) { if (isAmbiguousStaticLoggerBinderPathSet(staticLoggerBinderPathSet)) {
Util.report("Actual binding is of type [" + StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr() + "]"); Util.report("Actual binding is of type [" + StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr() + "]");

View File

@ -85,9 +85,27 @@ public class MDC {
private MDC() { private MDC() {
} }
/**
* As of SLF4J version 1.7.14, StaticMDCBinder classes shipping in various bindings
* come with a getSingleton() method. Previously only a public field called SINGLETON
* was available.
*
* @return MDCAdapter
* @throws NoClassDefFoundError in case no binding is available
* @since 1.7.14
*/
private static MDCAdapter bwCompatibleGetMDCAdapterFromBinder() throws NoClassDefFoundError {
try {
return StaticMDCBinder.getSingleton().getMDCA();
} catch (NoSuchMethodError nsme) {
// binding is probably a version of SLF4J older than 1.7.14
return StaticMDCBinder.SINGLETON.getMDCA();
}
}
static { static {
try { try {
mdcAdapter = StaticMDCBinder.SINGLETON.getMDCA(); mdcAdapter = bwCompatibleGetMDCAdapterFromBinder();
} catch (NoClassDefFoundError ncde) { } catch (NoClassDefFoundError ncde) {
mdcAdapter = new NOPMDCAdapter(); mdcAdapter = new NOPMDCAdapter();
String msg = ncde.getMessage(); String msg = ncde.getMessage();

View File

@ -42,17 +42,35 @@ import org.slf4j.impl.StaticMarkerBinder;
* @author Ceki G&uuml;lc&uuml; * @author Ceki G&uuml;lc&uuml;
*/ */
public class MarkerFactory { public class MarkerFactory {
static IMarkerFactory markerFactory; static IMarkerFactory MARKER_FACTORY;
private MarkerFactory() { private MarkerFactory() {
} }
/**
* As of SLF4J version 1.7.14, StaticMarkerBinder classes shipping in various bindings
* come with a getSingleton() method. Previously only a public field called SINGLETON
* was available.
*
* @return IMarkerFactory
* @throws NoClassDefFoundError in case no binding is available
* @since 1.7.14
*/
private static IMarkerFactory bwCompatibleGetMarkerFactoryFromBinder() throws NoClassDefFoundError {
try {
return StaticMarkerBinder.getSingleton().getMarkerFactory();
} catch (NoSuchMethodError nsme) {
// binding is probably a version of SLF4J older than 1.7.14
return StaticMarkerBinder.SINGLETON.getMarkerFactory();
}
}
// this is where the binding happens
static { static {
try { try {
markerFactory = StaticMarkerBinder.SINGLETON.getMarkerFactory(); MARKER_FACTORY = bwCompatibleGetMarkerFactoryFromBinder();
} catch (NoClassDefFoundError e) { } catch (NoClassDefFoundError e) {
markerFactory = new BasicMarkerFactory(); MARKER_FACTORY = new BasicMarkerFactory();
} catch (Exception e) { } catch (Exception e) {
// we should never get here // we should never get here
Util.report("Unexpected failure while binding MarkerFactory", e); Util.report("Unexpected failure while binding MarkerFactory", e);
@ -68,7 +86,7 @@ public class MarkerFactory {
* @return marker * @return marker
*/ */
public static Marker getMarker(String name) { public static Marker getMarker(String name) {
return markerFactory.getMarker(name); return MARKER_FACTORY.getMarker(name);
} }
/** /**
@ -79,7 +97,7 @@ public class MarkerFactory {
* @since 1.5.1 * @since 1.5.1
*/ */
public static Marker getDetachedMarker(String name) { public static Marker getDetachedMarker(String name) {
return markerFactory.getDetachedMarker(name); return MARKER_FACTORY.getDetachedMarker(name);
} }
/** /**
@ -91,6 +109,6 @@ public class MarkerFactory {
* @return the IMarkerFactory instance in use * @return the IMarkerFactory instance in use
*/ */
public static IMarkerFactory getIMarkerFactory() { public static IMarkerFactory getIMarkerFactory() {
return markerFactory; return MARKER_FACTORY;
} }
} }

View File

@ -42,6 +42,16 @@ public class StaticMDCBinder {
private StaticMDCBinder() { private StaticMDCBinder() {
throw new UnsupportedOperationException("This code should never make it into the jar"); throw new UnsupportedOperationException("This code should never make it into the jar");
} }
/**
* Return the singleton of this class.
*
* @return the StaticMDCBinder singleton
* @since 1.7.14
*/
public static final StaticMDCBinder getSingleton() {
return SINGLETON;
}
/** /**
* Currently this method always returns an instance of * Currently this method always returns an instance of

View File

@ -51,6 +51,16 @@ public class StaticMarkerBinder implements MarkerFactoryBinder {
throw new UnsupportedOperationException("This code should never make it into the jar"); throw new UnsupportedOperationException("This code should never make it into the jar");
} }
/**
* Return the singleton of this class.
*
* @return the StaticMarkerBinder singleton
* @since 1.7.14
*/
public static StaticMarkerBinder getSingleton() {
return SINGLETON;
}
/** /**
* Currently this method always returns an instance of * Currently this method always returns an instance of
* {@link BasicMarkerFactory}. * {@link BasicMarkerFactory}.

View File

@ -0,0 +1,141 @@
package org.slf4j;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* This class demonstrates that threads accessing the STATE variable always see a consistent value.
*
* During ongoing initialization the observed value is either ONGOING_INITIALIZATION
* or one of {SUCCESS, FAILURE}.
*
* Post initialization the observed value is always one of {SUCCESS, FAILURE}.
*
* See also http://jira.qos.ch/browse/SLF4J-167
*
* @author ceki
*
*/
public class DoubleCheckedInt {
final static int THREAD_COUNT = 10 + Runtime.getRuntime().availableProcessors() * 2;
final static int UNINITIALIZED_STATE = 0;
final static int ONGOING_INITIALIZATION = 1;
final static int SUCCESS = 2;
final static int FAILURE = 3;
final static int NUMBER_OF_STATES = FAILURE + 1;
private static int STATE = UNINITIALIZED_STATE;
public static int getState() {
if (STATE == 0) {
synchronized (DoubleCheckedInt.class) {
if (STATE == UNINITIALIZED_STATE) {
STATE = ONGOING_INITIALIZATION;
long r = System.nanoTime();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
if (r % 2 == 0) {
STATE = SUCCESS;
} else {
STATE = FAILURE;
}
}
}
}
return STATE;
}
static public void main(String[] args) throws InterruptedException, BrokenBarrierException {
StateAccessingThread[] preInitializationThreads = harness();
check(preInitializationThreads, false);
System.out.println("============");
StateAccessingThread[] postInitializationThreads = harness();
check(postInitializationThreads, true);
}
private static StateAccessingThread[] harness() throws InterruptedException, BrokenBarrierException {
StateAccessingThread[] threads = new StateAccessingThread[THREAD_COUNT];
final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i] = new StateAccessingThread(barrier);
threads[i].start();
}
barrier.await();
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i].join();
}
return threads;
}
private static void check(StateAccessingThread[] threads, boolean postInit) {
int[] stateCount = getStateCount(threads);
printStateCount(stateCount);
if (stateCount[UNINITIALIZED_STATE] != 0) {
throw new IllegalStateException("getState() should never return a zero value");
}
if (stateCount[SUCCESS] != 0 && stateCount[FAILURE] != 0) {
throw new IllegalStateException("getState() should return consistent values");
}
if (postInit) {
if (stateCount[SUCCESS] != THREAD_COUNT && stateCount[FAILURE] != THREAD_COUNT) {
throw new IllegalStateException("getState() should return consistent values");
}
}
}
private static void printStateCount(int[] stateCount) {
for (int i = 0; i < NUMBER_OF_STATES; i++) {
switch (i) {
case UNINITIALIZED_STATE:
System.out.println("UNINITIALIZED_STATE count: " + stateCount[i]);
break;
case ONGOING_INITIALIZATION:
System.out.println("ONGOING_INITIALIZATION count: " + stateCount[i]);
break;
case SUCCESS:
System.out.println("SUCCESS count: " + stateCount[i]);
break;
case FAILURE:
System.out.println("FAILURE count: " + stateCount[i]);
break;
}
}
}
private static int[] getStateCount(StateAccessingThread[] threads) {
int[] valCount = new int[NUMBER_OF_STATES];
for (int i = 0; i < threads.length; i++) {
int val = threads[i].state;
valCount[val] = valCount[val] + 1;
}
return valCount;
}
static class StateAccessingThread extends Thread {
public int state = -1;
final CyclicBarrier barrier;
StateAccessingThread(CyclicBarrier barrier) {
this.barrier = barrier;
}
public void run() {
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
state = DoubleCheckedInt.getState();
}
};
}

View File

@ -46,7 +46,7 @@ public class JCLLoggerFactory implements ILoggerFactory {
static { static {
try { try {
Class.forName("org.apache.commons.logging.impl.SLF4JLogFactory"); Class.forName("org.apache.commons.logging.impl.SLF4JLogFactory");
String part1 = "Detected both jcl-over-slf4j.jar AND slf4j-jcl.jar on the class path, preempting StackOverflowError. "; String part1 = "Detected both jcl-over-slf4j.jar AND bound slf4j-jcl.jar on the class path, preempting StackOverflowError. ";
String part2 = "See also " + JCL_DELEGATION_LOOP_URL + " for more details."; String part2 = "See also " + JCL_DELEGATION_LOOP_URL + " for more details.";
Util.report(part1); Util.report(part1);
@ -58,7 +58,7 @@ public class JCLLoggerFactory implements ILoggerFactory {
} }
// key: name (String), value: a JCLLoggerAdapter; // key: name (String), value: a JCLLoggerAdapter;
ConcurrentMap<String, Logger> loggerMap; final ConcurrentMap<String, Logger> loggerMap;
public JCLLoggerFactory() { public JCLLoggerFactory() {
loggerMap = new ConcurrentHashMap<String, Logger>(); loggerMap = new ConcurrentHashMap<String, Logger>();

View File

@ -42,11 +42,22 @@ public class StaticMDCBinder {
private StaticMDCBinder() { private StaticMDCBinder() {
} }
/**
* Return the singleton of this class.
*
* @return the StaticMDCBinder singleton
* @since 1.7.14
*/
public static final StaticMDCBinder getSingleton() {
return SINGLETON;
}
/** /**
* Currently this method always returns an instance of * Currently this method always returns an instance of
* {@link NOPMDCAdapter}. * {@link NOPMDCAdapter}.
* *
* @return instance of NOPMDCAdapter * @return instance of NOPMDCAdapter
* @since 1.7.14
*/ */
public MDCAdapter getMDCA() { public MDCAdapter getMDCA() {
return new NOPMDCAdapter(); return new NOPMDCAdapter();

View File

@ -48,6 +48,16 @@ public class StaticMarkerBinder implements MarkerFactoryBinder {
private StaticMarkerBinder() { private StaticMarkerBinder() {
} }
/**
* Return the singleton of this class.
*
* @return the StaticMarkerBinder singleton
* @since 1.7.14
*/
public static StaticMarkerBinder getSingleton() {
return SINGLETON;
}
/** /**
* Currently this method always returns an instance of * Currently this method always returns an instance of
* {@link BasicMarkerFactory}. * {@link BasicMarkerFactory}.

View File

@ -41,7 +41,17 @@ public class StaticMDCBinder {
private StaticMDCBinder() { private StaticMDCBinder() {
} }
/**
* Return the singleton of this class.
*
* @return the StaticMDCBinder singleton
* @since 1.7.14
*/
public static final StaticMDCBinder getSingleton() {
return SINGLETON;
}
/** /**
* Currently this method always returns an instance of * Currently this method always returns an instance of
* {@link BasicMDCAdapter}. * {@link BasicMDCAdapter}.

View File

@ -48,6 +48,16 @@ public class StaticMarkerBinder implements MarkerFactoryBinder {
private StaticMarkerBinder() { private StaticMarkerBinder() {
} }
/**
* Return the singleton of this class.
*
* @return the StaticMarkerBinder singleton
* @since 1.7.14
*/
public static StaticMarkerBinder getSingleton() {
return SINGLETON;
}
/** /**
* Currently this method always returns an instance of * Currently this method always returns an instance of
* {@link BasicMarkerFactory}. * {@link BasicMarkerFactory}.

View File

@ -28,6 +28,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import org.apache.log4j.LogManager; import org.apache.log4j.LogManager;
import org.slf4j.helpers.Util;
import org.slf4j.ILoggerFactory; import org.slf4j.ILoggerFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -39,6 +40,23 @@ import org.slf4j.Logger;
*/ */
public class Log4jLoggerFactory implements ILoggerFactory { public class Log4jLoggerFactory 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; // key: name (String), value: a Log4jLoggerAdapter;
ConcurrentMap<String, Logger> loggerMap; ConcurrentMap<String, Logger> loggerMap;

View File

@ -40,7 +40,17 @@ public class StaticMDCBinder {
private StaticMDCBinder() { private StaticMDCBinder() {
} }
/**
* Return the singleton of this class.
*
* @return the StaticMDCBinder singleton
* @since 1.7.14
*/
public static final StaticMDCBinder getSingleton() {
return SINGLETON;
}
/** /**
* Currently this method always returns an instance of * Currently this method always returns an instance of
* {@link StaticMDCBinder}. * {@link StaticMDCBinder}.

View File

@ -48,6 +48,16 @@ public class StaticMarkerBinder implements MarkerFactoryBinder {
private StaticMarkerBinder() { private StaticMarkerBinder() {
} }
/**
* Return the singleton of this class.
*
* @return the StaticMarkerBinder singleton
* @since 1.7.14
*/
public static StaticMarkerBinder getSingleton() {
return SINGLETON;
}
/** /**
* Currently this method always returns an instance of * Currently this method always returns an instance of
* {@link BasicMarkerFactory}. * {@link BasicMarkerFactory}.

View File

@ -42,6 +42,16 @@ public class StaticMDCBinder {
private StaticMDCBinder() { private StaticMDCBinder() {
} }
/**
* Return the singleton of this class.
*
* @return the StaticMDCBinder singleton
* @since 1.7.14
*/
public static final StaticMDCBinder getSingleton() {
return SINGLETON;
}
/** /**
* Currently this method always returns an instance of * Currently this method always returns an instance of
* {@link StaticMDCBinder}. * {@link StaticMDCBinder}.

View File

@ -48,6 +48,16 @@ public class StaticMarkerBinder implements MarkerFactoryBinder {
private StaticMarkerBinder() { private StaticMarkerBinder() {
} }
/**
* Return the singleton of this class.
*
* @return the StaticMarkerBinder singleton
* @since 1.7.14
*/
public static StaticMarkerBinder getSingleton() {
return SINGLETON;
}
/** /**
* Currently this method always returns an instance of * Currently this method always returns an instance of
* {@link BasicMarkerFactory}. * {@link BasicMarkerFactory}.

View File

@ -42,6 +42,16 @@ public class StaticMDCBinder {
private StaticMDCBinder() { private StaticMDCBinder() {
} }
/**
* Return the singleton of this class.
*
* @return the StaticMDCBinder singleton
* @since 1.7.14
*/
public static final StaticMDCBinder getSingleton() {
return SINGLETON;
}
/** /**
* Currently this method always returns an instance of * Currently this method always returns an instance of
* {@link StaticMDCBinder}. * {@link StaticMDCBinder}.

View File

@ -48,7 +48,17 @@ public class StaticMarkerBinder implements MarkerFactoryBinder {
private StaticMarkerBinder() { private StaticMarkerBinder() {
} }
/**
* Return the singleton of this class.
*
* @return the StaticMarkerBinder singleton
* @since 1.7.14
*/
public static StaticMarkerBinder getSingleton() {
return SINGLETON;
}
/** /**
* Currently this method always returns an instance of * Currently this method always returns an instance of
* {@link BasicMarkerFactory}. * {@link BasicMarkerFactory}.

View File

@ -323,10 +323,11 @@ class B extends A {
<p>The purpose of slf4j-log4j12 module is to delegate or redirect <p>The purpose of slf4j-log4j12 module is to delegate or redirect
calls made to an SLF4J logger to log4j. The purpose of the calls made to an SLF4J logger to log4j. The purpose of the
log4j-over-slf4j module is to redirect calls made to a log4j log4j-over-slf4j module is to redirect calls made to a log4j
logger to SLF4J. If both <em>slf4j-log4j12.jar</em> and logger to SLF4J. If SLF4J is bound with<em>slf4j-log4j12.jar</em>
<em>log4j-over-slf4j.jar</em> are present on the class path, a and <em>log4j-over-slf4j.jar</em> is also present on the class
<code>StackOverflowError</code> will inevitably occur immediately path, a <code>StackOverflowError</code> will inevitably occur
after the first invocation of an SLF4J or a log4j logger. immediately after the first invocation of an SLF4J or a log4j
logger.
</p> </p>
<p>Here is how the exception might look like:</p> <p>Here is how the exception might look like:</p>
@ -371,11 +372,11 @@ class B extends A {
<p>The purpose of slf4j-jcl module is to delegate or redirect <p>The purpose of slf4j-jcl module is to delegate or redirect
calls made to an SLF4J logger to jakarta commons logging calls made to an SLF4J logger to jakarta commons logging
(JCL). The purpose of the jcl-over-slf4j module is to redirect (JCL). The purpose of the jcl-over-slf4j module is to redirect
calls made to a JCL logger to SLF4J. If both calls made to a JCL logger to SLF4J. If SLF4J is bound with
<em>slf4j-jcl.jar</em> and <em>jcl-over-slf4j.jar</em> are present <em>slf4j-jcl.jar</em> and <em>jcl-over-slf4j.jar</em> is also
on the class path, then a <code>StackOverflowError</code> will present on the class path, then a <code>StackOverflowError</code>
inevitably occur immediately after the first invocation of an will inevitably occur immediately after the first invocation of an
SLF4J or a JCL logger. SLF4J or a JCL logger.
</p> </p>
<p>Here is how the exception might look like:</p> <p>Here is how the exception might look like:</p>

View File

@ -29,6 +29,52 @@
<hr noshade="noshade" size="1"/> <hr noshade="noshade" size="1"/>
<h3>xxx January, 2016 - Release of SLF4J 1.7.14</h3>
<p>The assignment of the INITIALIZATION_STATE variable in
<code>LoggerFactory</code> is now guaranteed to be consistent for
multi-thread initializations. More specifically, only one thread
will see INITIALIZATION_STATE as UNINITIALIZED with all other
threads observing either ONGOING_INITIALIZATION or the final result
of the initialization. However, SLF4J initialization is still
non-blocking and re-entrant, in the sense that if some thread tries
to obtain loggers during ongoing initialization by another (or
same) thread, instances of <code>SubstituteLogger</code> are
returned. This fixes <a
href="http://jira.qos.ch/browse/SLF4J-167">SLF4J-167</a> </p>.
<p>Moved delegation check loop from <em>log4j-over-slf4j.jar</em>
to <em>slf4j-log4j12.jar</em> for better targeted loop checks. The
rationale behind the change is explained by Frans Orsel in <a
href="http://jira.qos.ch/browse/SLF4J-345">SLF4J-345</a> who also
provided the relevant pull request.
</p>
<p>During initialization the binding ambiguity check is skipped
under Android in order to improve performance. This change was
requested by Nitin Verma in <a
href="http://jira.qos.ch/browse/SLF4J-328">SLF4J-328</a> who also
provided the relevant patch.
</p>
<p>The <code>StaticMarkerBinder</code> and
<code>StaticMDCBinder</code> classes shipping in various SLF4J
bindings now offer a <code>getSingletion()</code> method. The
<code>org.slf4j.MDC</code> and <code>MarkerFactory</code> classes
now perform binding by invoking the <code>getSingleton()</code>
method first and if in the presence of an older version of SLF4J
with said method missing, then by accessing the SINGLETON
field. This backward compatible change was requested by Rufus
Alexander in <a
href="http://jira.qos.ch/browse/SLF4J-347">SLF4J-347</a> in order
to implement an SLF4J binding in Clojure, namely <a
href="https://github.com/fzakaria/slf4j-timbre">slf4j-timbre</a>.
</p>
<hr noshade="noshade" size="1"/>
<h3>10th of November, 2015 - Release of SLF4J 1.7.13</h3> <h3>10th of November, 2015 - Release of SLF4J 1.7.13</h3>