Given the huge size of the SLF4J user base, we need a more lenient version

check mechanism. For example, we can't expect external implementations such as
Mina, jetty, jboss to follow SLF4J's release schedule. We just can't.

This commit changes the version check policy to become elective. The FAQ includes an entry explaining 
the logic.
This commit is contained in:
Ceki Gulcu 2008-10-17 14:31:02 +00:00
parent c7268f52fa
commit 2050a4db44
31 changed files with 194 additions and 139 deletions

View File

@ -5,6 +5,9 @@ if ($#ARGV < 1) {
}
$V= $ARGV[0];
# Trim -SNAPSHOT
$V =~ s/-SNAPSHOT//;
print "VER:${V}\r\n";
shift(@ARGV);

View File

@ -4,4 +4,4 @@ echo "Changing pom.xml files"
find . -name "pom.xml" |xargs perl version.pl ${VER}
echo "Changing Java files"
find . -name "StaticLoggerBinder.java" |xargs perl binderVersion.pl ${VER}
find slf4j-api -name "LoggerFactory.java" |xargs perl binderVersion.pl ${VER}

View File

@ -38,15 +38,15 @@
</target>
<target name="testAll" depends="init,
testPre154,
testPre155,
testMatch">
</target>
<target name="testPre154">
<target name="testPre155">
<junit printsummary="yes" fork="no" haltonfailure="yes">
<classpath refid="path150" />
<formatter type="plain" />
<test fork="yes" todir="target/unit-reports" name="org.slf4j.Pre154VersionMismatchTest" />
<test fork="yes" todir="target/unit-reports" name="org.slf4j.Pre155VersionMismatchTest" />
</junit>
</target>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.5.4</version>
<version>1.5.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,14 +5,13 @@ import java.util.Random;
import junit.framework.TestCase;
public class Pre154VersionMismatchTest extends TestCase {
public class Pre155VersionMismatchTest extends TestCase {
StringPrintStream sps = new StringPrintStream(System.err);
PrintStream old = System.err;
int diff = 1024 + new Random().nextInt(10000);
public Pre154VersionMismatchTest(String name) {
public Pre155VersionMismatchTest(String name) {
super(name);
}
@ -26,18 +25,11 @@ public class Pre154VersionMismatchTest extends TestCase {
System.setErr(old);
}
public void test() throws Exception {
public void test() throws Exception {
Logger logger = LoggerFactory.getLogger(this.getClass());
String msg = "hello world "+diff;
String msg = "hello world " + diff;
logger.info(msg);
String s0 = (String) sps.stringList.get(0);
assertTrue(s0.startsWith("SLF4J: The version of your slf4j-binding is probably older than 1.5.4"));
String s1 = (String) sps.stringList.get(1);
assertTrue(s1.contains(LoggerFactory.VERSION_MISMATCH));
String s2 = (String) sps.stringList.get(2);
assertTrue(s2.contains(msg));
assertTrue(s0.contains(msg));
}
}

View File

@ -3,7 +3,7 @@
<parent>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.5.4</version>
<version>1.5.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -3,7 +3,7 @@
<parent>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.5.4</version>
<version>1.5.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.5.4</version>
<version>1.5.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.5.4</version>
<version>1.5.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -3,7 +3,7 @@
<parent>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.5.4</version>
<version>1.5.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.5.4</version>
<version>1.5.5-SNAPSHOT</version>
<packaging>pom</packaging>
<name>SLF4J</name>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.5.4</version>
<version>1.5.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -25,6 +25,7 @@
package org.slf4j;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.slf4j.helpers.SubstituteLoggerFactory;
@ -56,7 +57,13 @@ public final class LoggerFactory {
static final String VERSION_MISMATCH = "http://www.slf4j.org/codes.html#version_mismatch";
static final String SUBSTITUTE_LOGGER_URL = "http://www.slf4j.org/codes.html#substituteLogger";
static private final String EXPECTED_VERSION = "1.5.4";
/**
* It is out responsibility to track version changes and manage the
* compatibility list.
*
* <p>
*/
static private final String[] API_COMPATIBILITY_LIST = new String[] { "1.5.5" };
// private constructor prevents instantiation
private LoggerFactory() {
@ -95,7 +102,7 @@ public final class LoggerFactory {
}
private final static void emitSubstitureLoggerWarning(List loggerNameList) {
if(loggerNameList.size() == 0) {
if (loggerNameList.size() == 0) {
return;
}
Util
@ -111,22 +118,29 @@ public final class LoggerFactory {
private final static void versionSanityCheck() {
try {
String actualVer = StaticLoggerBinder.VERSION;
if (!EXPECTED_VERSION.equals(actualVer)) {
Util.reportFailure("The version " + actualVer
+ " of your slf4j-binding differs from " + EXPECTED_VERSION
+ ", the expected version.");
String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
boolean match = false;
for (int i = 0; i < API_COMPATIBILITY_LIST.length; i++) {
if (API_COMPATIBILITY_LIST[i].equals(requested)) {
match = true;
}
}
if (!match) {
Util.reportFailure("The requested version " + requested
+ " of your slf4j-binding does not match any of "
+ Arrays.toString(API_COMPATIBILITY_LIST));
Util.reportFailure("See " + VERSION_MISMATCH + " for further details.");
}
} catch (java.lang.NoSuchFieldError nsfe) {
Util
.reportFailure("The version of your slf4j-binding is probably older than 1.5.4, and differs from "
+ EXPECTED_VERSION + ", the expected version.");
Util.reportFailure("See " + VERSION_MISMATCH + " for further details.");
// given our large user base anbd SLF4J's commitment to backward
// compatibility, we cannot cry
// here. Only for implementations which willingly declare a
// REQUESTED_API_VERSION field do we emit compatibility warnings.
} catch (Throwable e) {
Util
.reportFailure("An unexpected problem occured while checking the version of your slf4j-binding");
e.printStackTrace();
// we should never reach here
Util.reportFailure(
"Unexpected problem occured during version sanity check", e);
}
}

View File

@ -44,11 +44,11 @@ public class StaticLoggerBinder {
public static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
/**
* Version tag used to check compatibility. The value of this field is
* modified with each release.
* Declare the version of the SLF4J API this implementation is compiled against.
* The value of this field is usually modified with each release.
*/
// to avoid constant folding by the compiler, VERSION field should *not* be final
public static String VERSION = "1.5.4"; // !final
// to avoid constant folding by the compiler, this field must *not* be final
public static String REQUESTED_API_VERSION = "1.5.5"; // !final
private StaticLoggerBinder() {
throw new UnsupportedOperationException("This code should have never made it into the jar");

View File

@ -5,7 +5,7 @@
<parent>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.5.4</version>
<version>1.5.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -3,7 +3,7 @@
<parent>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.5.4</version>
<version>1.5.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -54,8 +54,9 @@ public class StaticLoggerBinder implements LoggerFactoryBinder {
* Version tag used to check compatibility. The value of this field is
* modified with each release.
*/
//to avoid constant folding by the compiler, VERSION field should *not* be final
public static String VERSION = "1.5.4";
//to avoid constant folding by the compiler, this field must *not* be final
public static String REQUESTED_API_VERSION = "1.5.5";
// Binding specific code:
private static final String loggerFactoryClassStr = JCLLoggerFactory.class

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.5.4</version>
<version>1.5.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -49,13 +49,14 @@ public class StaticLoggerBinder implements LoggerFactoryBinder {
* The unique instance of this class.
*/
public static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
/**
* Version tag used to check compatibility. The value of this field is
* modified with each release.
*/
//to avoid constant folding by the compiler, VERSION field should *not* be final
public static String VERSION = "1.5.4";
* Declare the version of the SLF4J API this implementation is compiled against.
* The value of this field is usually modified with each release.
*/
// to avoid constant folding by the compiler, this field must *not* be final
public static String REQUESTED_API_VERSION = "1.5.5"; // !final
private static final String loggerFactoryClassStr = org.slf4j.impl.JDK14LoggerFactory.class.getName();

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.5.4</version>
<version>1.5.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -52,10 +52,11 @@ public class StaticLoggerBinder implements LoggerFactoryBinder {
public static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
/**
* Version tag used to check compatibility. The value of this field is
* modified in each release.
* Declare the version of the SLF4J API this implementation is compiled against.
* The value of this field is usually modified with each release.
*/
public static final String VERSION = "1.5.4";
// to avoid constant folding by the compiler, this field must *not* be final
public static String REQUESTED_API_VERSION = "1.5.5"; // !final
private static final String loggerFactoryClassStr = Log4jLoggerFactory.class.getName();

View File

@ -7,7 +7,7 @@
<parent>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.5.4</version>
<version>1.5.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.5.4</version>
<version>1.5.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -51,11 +51,11 @@ public class StaticLoggerBinder implements LoggerFactoryBinder {
public static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
/**
* Version tag used to check compatibility. The value of this field is
* modified with each release.
*/
//to avoid constant folding by the compiler, VERSION field should *not* be final
public static String VERSION = "1.5.4";
* Declare the version of the SLF4J API this implementation is compiled against.
* The value of this field is usually modified with each release.
*/
// to avoid constant folding by the compiler, this field must *not* be final
public static String REQUESTED_API_VERSION = "1.5.5"; // !final
private static final String loggerFactoryClassStr = NOPLoggerFactory.class.getName();

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.5.4</version>
<version>1.5.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.5.4</version>
<version>1.5.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -3,7 +3,7 @@
<parent>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.5.4</version>
<version>1.5.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -43,11 +43,11 @@ public class StaticLoggerBinder implements LoggerFactoryBinder {
public static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
/**
* Version tag used to check compatibility. The value of this field is
* modified with each release.
*/
//to avoid constant folding by the compiler, VERSION field should *not* be final
public static String VERSION = "1.5.4";
* Declare the version of the SLF4J API this implementation is compiled against.
* The value of this field is usually modified with each release.
*/
// to avoid constant folding by the compiler, this field must *not* be final
public static String REQUESTED_API_VERSION = "1.5.5"; // !final
private static final String loggerFactoryClassStr = SimpleLoggerFactory.class.getName();

View File

@ -5,7 +5,7 @@
<parent>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.5.4</version>
<version>1.5.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -164,9 +164,9 @@ prefix='';
version does not match that of the binding</a></h3>
<p>Mixing mixing different versions of slf4j artifacts can cause
problems. For example, if you are using slf4j-api-1.5.4.jar, then
you should also use slf4j-simple-1.5.4.jar, using
slf4j-simple-1.4.2.jar will not work.
problems. For example, if you are using slf4j-api-1.5.5.jar, then
you should also use slf4j-simple-1.5.5.jar, using
slf4j-simple-1.4.2.jar will not work.
</p>
<p>In general, you should take sure that the slf4j-api version
@ -178,43 +178,42 @@ prefix='';
</p>
<h3><a name="substituteLogger" href="#substituteLogger">Substitute
loggers were created during the default configuration phase of the
underlying logging system</a></h3>
<h3><a name="substituteLogger" href="#substituteLogger">Substitute
loggers were created during the default configuration phase of the
underlying logging system</a></h3>
<p>Highly configurable logging systems such as logback and log4j may
create components which invoke loggers during their own
initialization. See issue <a
href="http://jira.qos.ch/browse/LBCORE-47">lbcore-47</a> for a
typical occurrence. However, since the binding process with SLF4J has
not yet completed (because the underlying logging system was not yet
completely loaded into memory), it is not possible to honor such
logger creation requests, resulting in a
<code>NullPointerException</code>.</p>
<p>Highly configurable logging systems such as logback and log4j
may create components which invoke loggers during their own
initialization. See issue <a
href="http://jira.qos.ch/browse/LBCORE-47">lbcore-47</a> for a
typical occurrence. However, since the binding process with SLF4J
has not yet completed (because the underlying logging system was
not yet completely loaded into memory), it is not possible to
honor such logger creation requests, resulting in a
<code>NullPointerException</code>.</p>
<p>To avoid this chicken-and-egg problem, SLF4J substitutes a
no-operation logger factory during this initialization
phase. However, the loggers returned during this phase by the
substitute logger factory are not operational. They are nop
implementations.
</p>
<p>To avoid this chicken-and-egg problem, SLF4J substitutes a
no-operation logger factory during this initialization
phase. However, the substitute loggers returned during this phase
are not operational. They are nop implementations.
</p>
<p>If any substitute logger had to be created, SLF4J will emit a
warning listing such nop loggers. This warning is intended to let
you know that you should not expect any logging output from these
loggers.
</p>
<p>If any substitute logger had to be created, SLF4J will emit a
warning listing such nop loggers. This warning is intended to let
you know that you should not expect any logging output from these
loggers.
</p>
<p>The only way to obtain output from the listed loggers, is to
isolate the components invoking these loggers and to exclude them
from the default configuration. Both logback and log4j allow
multi-step configuration. It follows that the problematic components
should be configured in a second step separate from default
configuration.
</p>
<p>The only way to obtain output from the listed loggers, is to
isolate the components invoking these loggers and to exclude them
from the default configuration. Both logback and log4j allow
multi-step configuration. It follows that the problematic
components should be configured in a second step separate from
default configuration.
</p>
<p>If you are not interested in the output from any of the
substitute loggers, then no action is required on your part.</p>
<p>If you are not interested in the output from any of the
substitute loggers, then no action is required on your part.</p>

View File

@ -133,6 +133,10 @@ prefix='';
<li><a href="#marker_interface"> How can my logging system add
support for the <code>Marker</code> interface? </a></li>
<li><a href="#version_checks">How does SLF4J's version check
mechanism work? </a></li>
</ol>
@ -910,7 +914,7 @@ class MyClass {
<code>org.slf4j.Logger</code> interface. Refer to slf4j-jcl,
slf4j-jdk14, and slf4j-log4j12 modules for examples of
adapters.
</p>
</p>
<p>Once you have written an appropriate adapter, say
<code>MyLoggerAdapter</code>, you need to provide a factory
@ -918,7 +922,7 @@ class MyClass {
interface. This factory should return instances
<code>MyLoggerAdapter</code>. Let <code>MyLoggerFactory</code>
be the name of your factory class.
</p>
</p>
<p>Once you have the adapter, namely
<code>MyLoggerAdapter</code>, and a factory, namely
@ -942,7 +946,7 @@ class MyClass {
<code>org.slf4j.Logger</code> interface
</li>
<li>create a factory for the adapter created in the previous step,</li>
<li>>modify <code>StaticLoggerBinder</code> class to use the
<li>modify <code>StaticLoggerBinder</code> class to use the
factory you created in the previous step</li>
</ol>
@ -961,37 +965,77 @@ class MyClass {
allowed to ignore marker data passed by the user.
</p>
<p>However, even though marker data may be ignored, the user
must still be allowed to specify marker data. Otherwise, users
would not be able to switch between logging systems that
support markers and those that do not. In order to provide
minimal support for markers, SLF4J conforming systems need to
to include certain Marker related classes, namely,
<code>org.slf4j.Marker</code>,
<code>org.slf4j.IMarkerFactory</code>,
<code>org.slf4j.MarkerFactory</code>,
<code>org.slf4j.impl.BasicMarker</code>,
<code>org.slf4j.impl.BasicMarkerFactory</code>,
<code>org.slf4j.impl.MarkerIgnoringBase</code>,
<code>org.slf4j.impl.StaticMarkerBinder</code> and
<code>org.slf4j.spi.MarkerFactoryBinder</code>. Al of these
classes are available in the SLF4J subversion repository.
</p>
<p>However, even though marker data may be ignored, the user
must still be allowed to specify marker data. Otherwise, users
would not be able to switch between logging systems that
support markers and those that do not.
</p>
<p>The <code>MarkerIgnoringBase</code> class can serve as a
base for adapters or native implementations of logging systems
lacking marker support. In <code>MarkerIgnoringBase</code>,
methods taking marker data simply invoke the corresponding
method without the Marker argument, discarding any Marker data
passed as argument. Your SLF4J adapters can extend
<code>MarkerIgnoringBase</code> to quickly implement the
methods in <code>org.slf4j.Logger</code> which take a
<code>Marker</code> as the first argument.
</p>
<p>The <code>MarkerIgnoringBase</code> class can serve as a
base for adapters or native implementations of logging
systems lacking marker support. In
<code>MarkerIgnoringBase</code>, methods taking marker data
simply invoke the corresponding method without the Marker
argument, discarding any Marker data passed as
argument. Your SLF4J adapters can extend
<code>MarkerIgnoringBase</code> to quickly implement the
methods in <code>org.slf4j.Logger</code> which take a
<code>Marker</code> as the first argument.
</p>
<hr/>
</dd>
<dt><a name="version_checks" href="#version_checks"> How does
SLF4J's version check mechanism work?</a></dt>
<dd>
<p>Given its huge installed user base, the version check
performed by SLF4J API during its initialization is an
elective process. Conforming SLF4J implementations may choose
<em>not</em> to participate, in which case, no version check
will be performed.
</p>
<p>However, if you decide to participate, your SLF4J binding
needs to declare a variable called REQUESTED_API_VERSION
within your copy of the <code>StaticLoggerBinder</code>
class. The value of this variable should be equal to the
version of the slf4j-api.jar you are compiling against. If you
ugrade to a newer version of slf4j-api, you also need to
update the value of REQUESTED_API_VERSION. (That is all you
have to do.)
</p>
<p>For earch version, SLF4J API maintains a list of compatible
versions. SLF4J will emit a version mismatch warning only if
the requested version is not found in the compatibility
list. So even if your SLF4J binding has a different release
schedule than SLF4J, you can still participate in the version
check without incurring a mismatch warning. For example,
logback has a different release schedule but still
participates in version checks.</p>
<p><b>As of SLF4J 1.5.5</b>, all bindings shipped within SLF4J
distribution, e.g. slf4j-logj12, slf4j-simple and slf4j-jdk14,
declare the REQUESTED_API_VERSION field with a value equal to
their SLF4J version. It follows that, for example if
slf4j-simple-1.5.6.jar is mixed with simple-api-1.5.5.jar,
then a version mismatch warning will be issued. Note that
SLF4J prior to 1.5.5 did not have a version check
mechanism. (Actually, version 1.5.4 offered a check policy
which was much too restritive and inconsistent with the size
of our user base. Consequently, SLF4J version 1.5.5 was
released just a day after 1.5.4.)
</p>
<p>
</p>
</dd>
</dl>
</div>
<div class="section">