Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SLF4J-450] Allow binding to be explicitly specified #241

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
38 changes: 38 additions & 0 deletions slf4j-api/src/main/java/org/slf4j/LoggerFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@
package org.slf4j;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
Expand Down Expand Up @@ -79,6 +82,10 @@ public final class LoggerFactory {
static final String UNSUCCESSFUL_INIT_URL = CODES_PREFIX + "#unsuccessfulInit";
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>
*/
static final String BINDING_PROP = "slf4j.binding";

static final int UNINITIALIZED = 0;
static final int ONGOING_INITIALIZATION = 1;
Expand Down Expand Up @@ -143,6 +150,16 @@ 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 @@ -168,6 +185,27 @@ private final static void bind() {
}
}

static SLF4JServiceProvider loadExplicitlySpecified(String explicitlySpecified) {
if (null == explicitlySpecified) {
return null;
}
try {
Class<?> clazz = Class.forName(explicitlySpecified);
Constructor<?> constructor = clazz.getConstructor();
constructor.setAccessible(true);
Object provider = constructor.newInstance();
return (SLF4JServiceProvider) provider;
} catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
String message = String.format("Failed to instantiate the specified SLF4JServiceProvider (%s)", explicitlySpecified);
Util.report(message, e);
return null;
} catch (ClassCastException e) {
String message = String.format("Specified SLF4JServiceProvider (%s) does not implement SLF4JServiceProvider interface", explicitlySpecified);
Util.report(message, e);
return null;
}
}

private static void reportIgnoredStaticLoggerBinders(Set<URL> staticLoggerBinderPathSet) {
if (staticLoggerBinderPathSet.isEmpty()) {
return;
Expand Down
85 changes: 85 additions & 0 deletions slf4j-api/src/test/java/org/slf4j/LoggerFactoryTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package org.slf4j;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.spi.MDCAdapter;
import org.slf4j.spi.SLF4JServiceProvider;

import java.io.ByteArrayOutputStream;
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;

public class LoggerFactoryTest {
private PrintStream rawSyserr;
private ByteArrayOutputStream mockedSyserr;

@Before
public void setUp() {
rawSyserr = System.err;
mockedSyserr = new ByteArrayOutputStream();
System.setErr(new PrintStream(mockedSyserr));
}

@After
public void cleanUp() {
System.setErr(rawSyserr);
}

@Test
public void testExplicitlySpecified() {
assertThat(LoggerFactory.loadExplicitlySpecified("org.slf4j.LoggerFactoryTest$TestingProvider"),
is(instanceOf(TestingProvider.class)));
}

@Test
public void testExplicitlySpecifiedNull() {
assertNull(LoggerFactory.loadExplicitlySpecified(null));
}

@Test
public void testExplicitlySpecifyMissingServiceProvider() {
assertNull(LoggerFactory.loadExplicitlySpecified("com.example.ServiceProvider"));
assertThat(mockedSyserr.toString(),
containsString("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"));
}

public static class TestingProvider implements SLF4JServiceProvider {
@Override
public ILoggerFactory getLoggerFactory() {
return null;
}

@Override
public IMarkerFactory getMarkerFactory() {
return null;
}

@Override
public MDCAdapter getMDCAdapter() {
return null;
}

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

@Override
public void initialize() {

}
}
}
4 changes: 3 additions & 1 deletion slf4j-site/src/site/pages/faq.html
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,9 @@ <h2>Generalities</h2>

</table>


<p>It is also possible to specify the binding explicitly by the <code>slf4j.binding</code> system property.
It probably shortens the initialization phase, so could be valuable for systems which are sensitive to cold-start times like FaaS.
</p>
</dd>


Expand Down