diff --git a/integration/src/test/java/org/slf4j/MissingSingletonMethodAssertionTest.java b/integration/src/test/java/org/slf4j/MissingSingletonMethodAssertionTest.java index 96c2b416..251fa327 100644 --- a/integration/src/test/java/org/slf4j/MissingSingletonMethodAssertionTest.java +++ b/integration/src/test/java/org/slf4j/MissingSingletonMethodAssertionTest.java @@ -58,21 +58,32 @@ public class MissingSingletonMethodAssertionTest extends TestCase { } catch (NoSuchMethodError e) { } + for(String s: sps.stringList) { + System.out.println(s); + } int lineCount = sps.stringList.size(); assertTrue("number of lines should be 3 but was " + lineCount, lineCount == 3); - // expected output: + // expected output: (version 1.7 and earlier) // SLF4J: slf4j-api 1.6.x (or later) is incompatible with this binding. // SLF4J: Your binding is version 1.4.x or earlier. // SLF4J: Upgrade your binding to version 1.6.x. or 2.0.x + // expected output: (version 1.8) + // SLF4J: No SLF4J providers were found. + // SLF4J: Defaulting to no-operation (NOP) logger implementation + // SLF4J: See http://www.slf4j.org/codes.html#w for further details. + // SLF4J: Class path contains SLF4J bindings targeting slf4j-api versions prior to 1.8. + // SLF4J: Ignoring binding found at [jar:file:/C:/home/ceki/slf4j/integration/lib/slf4j-simple-1.4.2.jar!/org/slf4j/impl/StaticLoggerBinder.class] + // SLF4J: See http://www.slf4j.org/codes.html#ignoredBindings for an explanation. + { String s = (String) sps.stringList.get(0); - assertTrue(s.contains("SLF4J: slf4j-api 1.6.x (or later) is incompatible with this binding.")); + assertTrue(s.contains("No SLF4J providers were found.")); } { String s = (String) sps.stringList.get(1); - assertTrue(s.contains("SLF4J: Your binding is version 1.5.5 or earlier.")); + assertTrue(s.contains("Defaulting to no-operation (NOP) logger implementation")); } { String s = (String) sps.stringList.get(2); diff --git a/slf4j-api/src/main/java/org/slf4j/LoggerFactory.java b/slf4j-api/src/main/java/org/slf4j/LoggerFactory.java index 0748fc99..d94ee82c 100755 --- a/slf4j-api/src/main/java/org/slf4j/LoggerFactory.java +++ b/slf4j-api/src/main/java/org/slf4j/LoggerFactory.java @@ -24,19 +24,22 @@ */ package org.slf4j; +import java.io.IOException; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; +import java.util.Enumeration; +import java.util.LinkedHashSet; import java.util.List; import java.util.ServiceLoader; +import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; import org.slf4j.event.SubstituteLoggingEvent; -import org.slf4j.helpers.NOPLoggerFactory; +import org.slf4j.helpers.NOPServiceProvider; import org.slf4j.helpers.SubstitureServiceProvider; import org.slf4j.helpers.SubstituteLogger; -import org.slf4j.helpers.SubstituteLoggerFactory; import org.slf4j.helpers.Util; -import org.slf4j.helpers.NOPServiceProvider; import org.slf4j.spi.SLF4JServiceProvider; /** @@ -63,6 +66,9 @@ public final class LoggerFactory { static final String CODES_PREFIX = "http://www.slf4j.org/codes.html"; + 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"; @@ -139,13 +145,8 @@ public final class LoggerFactory { private final static void bind() { try { - List providersList = null; - // skip check under android, see also - // http://jira.qos.ch/browse/SLF4J-328 - if (!isAndroid()) { - providersList = findServiceProviders(); - reportMultipleBindingAmbiguity(providersList); - } + List providersList = findServiceProviders(); + reportMultipleBindingAmbiguity(providersList); if (providersList != null && !providersList.isEmpty()) { PROVIDER = providersList.get(0); INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION; @@ -156,7 +157,12 @@ public final class LoggerFactory { SUBST_PROVIDER.getSubstituteLoggerFactory().clear(); } else { INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION; - Util.report("No providers could be found."); + Util.report("No SLF4J providers were found."); + Util.report("Defaulting to no-operation (NOP) logger implementation"); + Util.report("See " + NO_PROVIDERS_URL + " for further details."); + + Set staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet(); + reportIgnoredStaticLoggerBinders(staticLoggerBinderPathSet); } } catch (Exception e) { failedBinding(e); @@ -164,6 +170,46 @@ public final class LoggerFactory { } } + private static void reportIgnoredStaticLoggerBinders(Set staticLoggerBinderPathSet) { + if (staticLoggerBinderPathSet.isEmpty()) { + return; + } + Util.report("Class path contains SLF4J bindings targeting slf4j-api versions prior to 1.8."); + for (URL path : staticLoggerBinderPathSet) { + Util.report("Ignoring binding found at [" + path + "]"); + } + Util.report("See " + IGNORED_BINDINGS_URL + " for an explanation."); + + + } + + // We need to use the name of the StaticLoggerBinder class, but we can't + // reference the class itself. + private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class"; + + static Set findPossibleStaticLoggerBinderPathSet() { + // use Set instead of list in order to deal with bug #138 + // LinkedHashSet appropriate here because it preserves insertion order + // during iteration + Set staticLoggerBinderPathSet = new LinkedHashSet(); + try { + ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader(); + Enumeration paths; + if (loggerFactoryClassLoader == null) { + paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH); + } else { + paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH); + } + while (paths.hasMoreElements()) { + URL path = paths.nextElement(); + staticLoggerBinderPathSet.add(path); + } + } catch (IOException ioe) { + Util.report("Error getting resources from path", ioe); + } + return staticLoggerBinderPathSet; + } + private static void fixSubstituteLoggers() { synchronized (SUBST_PROVIDER) { SUBST_PROVIDER.getSubstituteLoggerFactory().postInitialization(); @@ -279,7 +325,7 @@ public final class LoggerFactory { */ private static void reportMultipleBindingAmbiguity(List providerList) { if (isAmbiguousProviderList(providerList)) { - Util.report("Class path contains multiple SLF4J bindings."); + Util.report("Class path contains multiple SLF4J providers."); for (SLF4JServiceProvider provider : providerList) { Util.report("Found provider [" + provider + "]"); } @@ -287,13 +333,6 @@ 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(List providerList) { // binderPathSet can be null under Android if (!providerList.isEmpty() && isAmbiguousProviderList(providerList)) { @@ -363,8 +402,8 @@ public final class LoggerFactory { public static ILoggerFactory getILoggerFactory() { return getProvider().getLoggerFactory(); } - - static SLF4JServiceProvider getProvider() { + + static SLF4JServiceProvider getProvider() { if (INITIALIZATION_STATE == UNINITIALIZED) { synchronized (LoggerFactory.class) { if (INITIALIZATION_STATE == UNINITIALIZED) { diff --git a/slf4j-site/src/site/pages/codes.html b/slf4j-site/src/site/pages/codes.html index 83f26a35..5b4b4552 100755 --- a/slf4j-site/src/site/pages/codes.html +++ b/slf4j-site/src/site/pages/codes.html @@ -36,6 +36,59 @@ + + +

No SLF4J providers + were found. +

+ +

This warning, i.e. not an error, message is repoted when + no SLF4J providers could be found on the class path. Placing one + (and only one) of slf4j-nop.jar + slf4j-simple.jar, slf4j-log4j12.jar, + slf4j-jdk14.jar or logback-classic.jar on the + class path should solve the problem. Note that these providers + must target slf4j-api 1.8 or later. +

+ +

In the absence of a provider, SLF4J will default to a + no-operation (NOP) logger provider. +

+ +

Please note that slf4j-api version 1.8.x and later use the ServiceLoader + mechanism. Earlier versions relied on the static + binder mechanism which is no loger supported.

+ +

If you are responsible for packaging an application and do not + care about logging, then placing slf4j-nop.jar on the + class path of your application will get rid of this warning + message. Note that embedded components such as libraries or + frameworks should not declare a dependency on any SLF4J providers + but only depend on slf4j-api. When a library declares a + compile-time dependency on a SLF4J provider, it imposes that + provider on the end-user, thus negating SLF4J's purpose.

+ + + +

Class path contains + SLF4J bindings targeting slf4j-api versions prior to 1.8 +

+ +

Planning for the advent of Jigsaw (Java 9), slf4j-api version + 1.8.x and later use the ServiceLoader + mechanism. Earlier versions of SLF4J relied on + the static binder mechanism which is no loger + supported.

+ +

In case SLF4J finds no providers targeting SLF4J 1.8 but finds + instead bindings targeting SLF4J 1.7 or earlier, it will list the + bindings it finds but ignores. +

+ + +

IllegalStateException: