fine tuning for SLF4J-450

Signed-off-by: Ceki Gulcu <ceki@qos.ch>
This commit is contained in:
Ceki Gulcu 2023-08-29 22:06:48 +02:00
parent 9783320b6c
commit 05698f8d23
2 changed files with 58 additions and 48 deletions

View File

@ -53,21 +53,20 @@ import javax.accessibility.AccessibleComponent;
/**
* The <code>LoggerFactory</code> is a utility class producing Loggers for
* various logging APIs, most notably for log4j, logback and JDK 1.4 logging.
* various logging APIs, e.g. logback, reload4j, log4j and JDK 1.4 logging.
* Other implementations such as {@link org.slf4j.helpers.NOPLogger NOPLogger} and
* SimpleLogger are also supported.
*
* <p><code>LoggerFactory</code> is essentially a wrapper around an
* {@link ILoggerFactory} instance bound with <code>LoggerFactory</code> at
* compile time.
*
*
* <p><code>LoggerFactory</code> is essentially a wrapper around an
* {@link ILoggerFactory} instance provided by a {@link SLF4JServiceProvider}.
*
* <p>
* Please note that all methods in <code>LoggerFactory</code> are static.
*
*
* @author Alexander Dorokhine
* @author Robert Elliot
* @author Ceki G&uuml;lc&uuml;
*
*
*/
public final class LoggerFactory {
@ -76,9 +75,7 @@ public final class LoggerFactory {
static final String NO_PROVIDERS_URL = CODES_PREFIX + "#noProviders";
static final String IGNORED_BINDINGS_URL = CODES_PREFIX + "#ignoredBindings";
static final String NO_STATICLOGGERBINDER_URL = CODES_PREFIX + "#StaticLoggerBinder";
static final String MULTIPLE_BINDINGS_URL = CODES_PREFIX + "#multiple_bindings";
static final String NULL_LF_URL = CODES_PREFIX + "#null_LF";
static final String VERSION_MISMATCH = CODES_PREFIX + "#version_mismatch";
static final String SUBSTITUTE_LOGGER_URL = CODES_PREFIX + "#substituteLogger";
static final String LOGGER_NAME_MISMATCH_URL = CODES_PREFIX + "#loggerNameMismatch";
@ -88,9 +85,12 @@ public final class LoggerFactory {
static final String UNSUCCESSFUL_INIT_MSG = "org.slf4j.LoggerFactory in failed state. Original exception was thrown EARLIER. See also "
+ UNSUCCESSFUL_INIT_URL;
/**
* @see <a href="https://jira.qos.ch/browse/SLF4J-450">SLF4J-450</a>
* System property for explicitly setting the provider class. If set and the provider could be instantiated,
* then the service loading mechanism will be bypassed.
*
* @since 2.0.8
*/
static final String BINDING_PROP = "slf4j.binding";
static final public String BINDING_PROP = "slf4j.binding";
static final int UNINITIALIZED = 0;
static final int ONGOING_INITIALIZATION = 1;
@ -112,11 +112,21 @@ public final class LoggerFactory {
// Package access for tests
static List<SLF4JServiceProvider> findServiceProviders() {
List<SLF4JServiceProvider> providerList = new ArrayList<>();
// retain behaviour similar to that of 1.7 series and earlier. More specifically, use the class loader that
// loaded the present class to search for services
final ClassLoader classLoaderOfLoggerFactory = LoggerFactory.class.getClassLoader();
ServiceLoader<SLF4JServiceProvider> serviceLoader = getServiceLoader(classLoaderOfLoggerFactory);
List<SLF4JServiceProvider> providerList = new ArrayList<>();
SLF4JServiceProvider explicitProvider = loadExplicitlySpecified(classLoaderOfLoggerFactory);
if(explicitProvider != null) {
providerList.add(explicitProvider);
return providerList;
}
ServiceLoader<SLF4JServiceProvider> serviceLoader = getServiceLoader(classLoaderOfLoggerFactory);
Iterator<SLF4JServiceProvider> iterator = serviceLoader.iterator();
while (iterator.hasNext()) {
safelyInstantiate(providerList, iterator);
@ -179,16 +189,6 @@ public final class LoggerFactory {
}
private final static void bind() {
String explicitlySpecified = System.getProperty(BINDING_PROP);
PROVIDER = loadExplicitlySpecified(explicitlySpecified);
if (null != PROVIDER) {
PROVIDER.initialize();
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(Collections.singletonList(PROVIDER));
postBindCleanUp();
return;
}
try {
List<SLF4JServiceProvider> providersList = findServiceProviders();
reportMultipleBindingAmbiguity(providersList);
@ -214,14 +214,16 @@ public final class LoggerFactory {
}
}
static SLF4JServiceProvider loadExplicitlySpecified(String explicitlySpecified) {
if (null == explicitlySpecified) {
static SLF4JServiceProvider loadExplicitlySpecified(ClassLoader classLoader) {
String explicitlySpecified = System.getProperty(BINDING_PROP);
if (null == explicitlySpecified || explicitlySpecified.isEmpty()) {
return null;
}
try {
Class<?> clazz = Class.forName(explicitlySpecified);
String message = String.format("Attempting to load provider \"%s\" specified via \"%s\" system property", explicitlySpecified, BINDING_PROP);
Util.report(message);
Class<?> clazz = classLoader.loadClass(explicitlySpecified);
Constructor<?> constructor = clazz.getConstructor();
constructor.setAccessible(true);
Object provider = constructor.newInstance();
return (SLF4JServiceProvider) provider;
} catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
@ -395,7 +397,7 @@ public final class LoggerFactory {
/**
* Prints a warning message on the console if multiple bindings were found
* on the class path. No reporting is done otherwise.
*
*
*/
private static void reportMultipleBindingAmbiguity(List<SLF4JServiceProvider> providerList) {
if (isAmbiguousProviderList(providerList)) {
@ -417,7 +419,7 @@ public final class LoggerFactory {
/**
* Return a logger named according to the name parameter using the
* statically bound {@link ILoggerFactory} instance.
*
*
* @param name
* The name of the logger.
* @return logger
@ -430,7 +432,7 @@ public final class LoggerFactory {
/**
* Return a logger named corresponding to the class passed as parameter,
* using the statically bound {@link ILoggerFactory} instance.
*
*
* <p>
* In case the <code>clazz</code> parameter differs from the name of the
* caller as computed internally by SLF4J, a logger name mismatch warning
@ -438,12 +440,12 @@ public final class LoggerFactory {
* <code>slf4j.detectLoggerNameMismatch</code> system property is set to
* true. By default, this property is not set and no warnings will be
* printed even in case of a logger name mismatch.
*
*
* @param clazz
* the returned logger will be named after clazz
* @return logger
*
*
*
*
* @see <a
* href="http://www.slf4j.org/codes.html#loggerNameMismatch">Detected
* logger name mismatch</a>
@ -470,7 +472,7 @@ public final class LoggerFactory {
* <p>
* <p>
* ILoggerFactory instance is bound with this class at compile time.
*
*
* @return the ILoggerFactory instance in use
*/
public static ILoggerFactory getILoggerFactory() {
@ -479,7 +481,7 @@ public final class LoggerFactory {
/**
* Return the {@link SLF4JServiceProvider} in use.
* @return provider in use
* @since 1.8.0
*/

View File

@ -12,13 +12,14 @@ import java.io.PrintStream;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.*;
public class LoggerFactoryTest {
private PrintStream rawSyserr;
private ByteArrayOutputStream mockedSyserr;
final ClassLoader classLoaderOfLoggerFactory = LoggerFactory.class.getClassLoader();
@Before
public void setUp() {
rawSyserr = System.err;
@ -28,32 +29,39 @@ public class LoggerFactoryTest {
@After
public void cleanUp() {
System.clearProperty(LoggerFactory.BINDING_PROP);
System.setErr(rawSyserr);
}
@Test
public void testExplicitlySpecified() {
assertThat(LoggerFactory.loadExplicitlySpecified("org.slf4j.LoggerFactoryTest$TestingProvider"),
is(instanceOf(TestingProvider.class)));
System.setProperty(LoggerFactory.BINDING_PROP, "org.slf4j.LoggerFactoryTest$TestingProvider");
SLF4JServiceProvider provider = LoggerFactory.loadExplicitlySpecified(classLoaderOfLoggerFactory);
assertTrue("provider should be instance of TestingProvider class", provider instanceof TestingProvider);
assertTrue(mockedSyserr.toString().contains(" Attempting to load provider \"org.slf4j.LoggerFactoryTest$TestingProvider\" specified via \"slf4j.binding\" system property"));
System.out.println(mockedSyserr.toString());
}
@Test
public void testExplicitlySpecifiedNull() {
assertNull(LoggerFactory.loadExplicitlySpecified(null));
assertNull(LoggerFactory.loadExplicitlySpecified(classLoaderOfLoggerFactory));
}
@Test
public void testExplicitlySpecifyMissingServiceProvider() {
assertNull(LoggerFactory.loadExplicitlySpecified("com.example.ServiceProvider"));
assertThat(mockedSyserr.toString(),
containsString("Failed to instantiate the specified SLF4JServiceProvider (com.example.ServiceProvider)"));
System.setProperty(LoggerFactory.BINDING_PROP, "com.example.ServiceProvider");
SLF4JServiceProvider provider = LoggerFactory.loadExplicitlySpecified(classLoaderOfLoggerFactory);
assertNull(provider);
assertTrue(mockedSyserr.toString().contains("Failed to instantiate the specified SLF4JServiceProvider (com.example.ServiceProvider)"));
}
@Test
public void testExplicitlySpecifyNonServiceProvider() {
assertNull(LoggerFactory.loadExplicitlySpecified("java.lang.String"));
assertThat(mockedSyserr.toString(),
containsString("Specified SLF4JServiceProvider (java.lang.String) does not implement SLF4JServiceProvider interface"));
System.setProperty(LoggerFactory.BINDING_PROP, "java.lang.String");
assertNull(LoggerFactory.loadExplicitlySpecified(classLoaderOfLoggerFactory));
assertTrue(mockedSyserr.toString().contains("Specified SLF4JServiceProvider (java.lang.String) does not implement SLF4JServiceProvider interface"));
}
public static class TestingProvider implements SLF4JServiceProvider {
@ -73,7 +81,7 @@ public class LoggerFactoryTest {
}
@Override
public String getRequesteApiVersion() {
public String getRequestedApiVersion() {
return null;
}