Skip to content

Commit

Permalink
fine tuning for SLF4J-450
Browse files Browse the repository at this point in the history
Signed-off-by: Ceki Gulcu <ceki@qos.ch>
  • Loading branch information
ceki committed Aug 29, 2023
1 parent 9783320 commit 05698f8
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 48 deletions.
74 changes: 38 additions & 36 deletions slf4j-api/src/main/java/org/slf4j/LoggerFactory.java
Expand Up @@ -53,21 +53,20 @@

/**
* 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 {

Expand All @@ -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";
Expand All @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -179,16 +189,6 @@ private final static void performInitialization() {
}

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);
Expand All @@ -214,14 +214,16 @@ private final static void bind() {
}
}

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) {
Expand Down Expand Up @@ -395,7 +397,7 @@ private static boolean isAmbiguousProviderList(List<SLF4JServiceProvider> provid
/**
* 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)) {
Expand All @@ -417,7 +419,7 @@ private static void reportActualBinding(List<SLF4JServiceProvider> providerList)
/**
* 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
Expand All @@ -430,20 +432,20 @@ public static Logger getLogger(String name) {
/**
* 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
* will be printed but only if the
* <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>
Expand All @@ -470,7 +472,7 @@ private static boolean nonMatchingClasses(Class<?> clazz, Class<?> autoComputedC
* <p>
* <p>
* ILoggerFactory instance is bound with this class at compile time.
*
*
* @return the ILoggerFactory instance in use
*/
public static ILoggerFactory getILoggerFactory() {
Expand All @@ -479,7 +481,7 @@ public static ILoggerFactory getILoggerFactory() {

/**
* Return the {@link SLF4JServiceProvider} in use.
* @return provider in use
* @since 1.8.0
*/
Expand Down
32 changes: 20 additions & 12 deletions slf4j-api/src/test/java/org/slf4j/LoggerFactoryTest.java
Expand Up @@ -12,13 +12,14 @@
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;
Expand All @@ -28,32 +29,39 @@ public void setUp() {

@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 {
Expand All @@ -73,7 +81,7 @@ public MDCAdapter getMDCAdapter() {
}

@Override
public String getRequesteApiVersion() {
public String getRequestedApiVersion() {
return null;
}

Expand Down

0 comments on commit 05698f8

Please sign in to comment.