|
|
|
|
@ -63,400 +63,368 @@ import org.slf4j.impl.StaticLoggerBinder;
|
|
|
|
|
*/
|
|
|
|
|
public final class LoggerFactory {
|
|
|
|
|
|
|
|
|
|
static final String CODES_PREFIX = "http://www.slf4j.org/codes.html";
|
|
|
|
|
static final String CODES_PREFIX = "http://www.slf4j.org/codes.html";
|
|
|
|
|
|
|
|
|
|
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";
|
|
|
|
|
static final String REPLAY_URL = CODES_PREFIX + "#replay";
|
|
|
|
|
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";
|
|
|
|
|
static final String REPLAY_URL = CODES_PREFIX + "#replay";
|
|
|
|
|
|
|
|
|
|
static final String UNSUCCESSFUL_INIT_URL = CODES_PREFIX
|
|
|
|
|
+ "#unsuccessfulInit";
|
|
|
|
|
static final String UNSUCCESSFUL_INIT_MSG = "org.slf4j.LoggerFactory could not be successfully initialized. See also "
|
|
|
|
|
+ UNSUCCESSFUL_INIT_URL;
|
|
|
|
|
static final String UNSUCCESSFUL_INIT_URL = CODES_PREFIX + "#unsuccessfulInit";
|
|
|
|
|
static final String UNSUCCESSFUL_INIT_MSG = "org.slf4j.LoggerFactory could not be successfully initialized. See also " + UNSUCCESSFUL_INIT_URL;
|
|
|
|
|
|
|
|
|
|
static final int UNINITIALIZED = 0;
|
|
|
|
|
static final int ONGOING_INITIALIZATION = 1;
|
|
|
|
|
static final int FAILED_INITIALIZATION = 2;
|
|
|
|
|
static final int SUCCESSFUL_INITIALIZATION = 3;
|
|
|
|
|
static final int NOP_FALLBACK_INITIALIZATION = 4;
|
|
|
|
|
static final int UNINITIALIZED = 0;
|
|
|
|
|
static final int ONGOING_INITIALIZATION = 1;
|
|
|
|
|
static final int FAILED_INITIALIZATION = 2;
|
|
|
|
|
static final int SUCCESSFUL_INITIALIZATION = 3;
|
|
|
|
|
static final int NOP_FALLBACK_INITIALIZATION = 4;
|
|
|
|
|
|
|
|
|
|
static volatile int INITIALIZATION_STATE = UNINITIALIZED;
|
|
|
|
|
static SubstituteLoggerFactory SUBST_FACTORY = new SubstituteLoggerFactory();
|
|
|
|
|
static NOPLoggerFactory NOP_FALLBACK_FACTORY = new NOPLoggerFactory();
|
|
|
|
|
static volatile int INITIALIZATION_STATE = UNINITIALIZED;
|
|
|
|
|
static SubstituteLoggerFactory SUBST_FACTORY = new SubstituteLoggerFactory();
|
|
|
|
|
static NOPLoggerFactory NOP_FALLBACK_FACTORY = new NOPLoggerFactory();
|
|
|
|
|
|
|
|
|
|
// Support for detecting mismatched logger names.
|
|
|
|
|
static final String DETECT_LOGGER_NAME_MISMATCH_PROPERTY = "slf4j.detectLoggerNameMismatch";
|
|
|
|
|
static final String JAVA_VENDOR_PROPERTY = "java.vendor.url";
|
|
|
|
|
// Support for detecting mismatched logger names.
|
|
|
|
|
static final String DETECT_LOGGER_NAME_MISMATCH_PROPERTY = "slf4j.detectLoggerNameMismatch";
|
|
|
|
|
static final String JAVA_VENDOR_PROPERTY = "java.vendor.url";
|
|
|
|
|
|
|
|
|
|
static boolean DETECT_LOGGER_NAME_MISMATCH = Util
|
|
|
|
|
.safeGetBooleanSystemProperty(DETECT_LOGGER_NAME_MISMATCH_PROPERTY);
|
|
|
|
|
static boolean DETECT_LOGGER_NAME_MISMATCH = Util.safeGetBooleanSystemProperty(DETECT_LOGGER_NAME_MISMATCH_PROPERTY);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* It is LoggerFactory's responsibility to track version changes and manage
|
|
|
|
|
* the compatibility list.
|
|
|
|
|
* <p/>
|
|
|
|
|
* <p/>
|
|
|
|
|
* It is assumed that all versions in the 1.6 are mutually compatible.
|
|
|
|
|
*/
|
|
|
|
|
static private final String[] API_COMPATIBILITY_LIST = new String[] {
|
|
|
|
|
"1.6", "1.7" };
|
|
|
|
|
/**
|
|
|
|
|
* It is LoggerFactory's responsibility to track version changes and manage
|
|
|
|
|
* the compatibility list.
|
|
|
|
|
* <p/>
|
|
|
|
|
* <p/>
|
|
|
|
|
* It is assumed that all versions in the 1.6 are mutually compatible.
|
|
|
|
|
*/
|
|
|
|
|
static private final String[] API_COMPATIBILITY_LIST = new String[] { "1.6", "1.7" };
|
|
|
|
|
|
|
|
|
|
// private constructor prevents instantiation
|
|
|
|
|
private LoggerFactory() {
|
|
|
|
|
}
|
|
|
|
|
// private constructor prevents instantiation
|
|
|
|
|
private LoggerFactory() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Force LoggerFactory to consider itself uninitialized.
|
|
|
|
|
* <p/>
|
|
|
|
|
* <p/>
|
|
|
|
|
* This method is intended to be called by classes (in the same package) for
|
|
|
|
|
* testing purposes. This method is internal. It can be modified, renamed or
|
|
|
|
|
* removed at any time without notice.
|
|
|
|
|
* <p/>
|
|
|
|
|
* <p/>
|
|
|
|
|
* You are strongly discouraged from calling this method in production code.
|
|
|
|
|
*/
|
|
|
|
|
static void reset() {
|
|
|
|
|
INITIALIZATION_STATE = UNINITIALIZED;
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* Force LoggerFactory to consider itself uninitialized.
|
|
|
|
|
* <p/>
|
|
|
|
|
* <p/>
|
|
|
|
|
* This method is intended to be called by classes (in the same package) for
|
|
|
|
|
* testing purposes. This method is internal. It can be modified, renamed or
|
|
|
|
|
* removed at any time without notice.
|
|
|
|
|
* <p/>
|
|
|
|
|
* <p/>
|
|
|
|
|
* You are strongly discouraged from calling this method in production code.
|
|
|
|
|
*/
|
|
|
|
|
static void reset() {
|
|
|
|
|
INITIALIZATION_STATE = UNINITIALIZED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private final static void performInitialization() {
|
|
|
|
|
bind();
|
|
|
|
|
if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
|
|
|
|
|
versionSanityCheck();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private final static void performInitialization() {
|
|
|
|
|
bind();
|
|
|
|
|
if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
|
|
|
|
|
versionSanityCheck();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static boolean messageContainsOrgSlf4jImplStaticLoggerBinder(
|
|
|
|
|
String msg) {
|
|
|
|
|
if (msg == null)
|
|
|
|
|
return false;
|
|
|
|
|
if (msg.contains("org/slf4j/impl/StaticLoggerBinder"))
|
|
|
|
|
return true;
|
|
|
|
|
if (msg.contains("org.slf4j.impl.StaticLoggerBinder"))
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
private static boolean messageContainsOrgSlf4jImplStaticLoggerBinder(String msg) {
|
|
|
|
|
if (msg == null)
|
|
|
|
|
return false;
|
|
|
|
|
if (msg.contains("org/slf4j/impl/StaticLoggerBinder"))
|
|
|
|
|
return true;
|
|
|
|
|
if (msg.contains("org.slf4j.impl.StaticLoggerBinder"))
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private final static void bind() {
|
|
|
|
|
try {
|
|
|
|
|
Set<URL> staticLoggerBinderPathSet = null;
|
|
|
|
|
// skip check under android, see also
|
|
|
|
|
// http://jira.qos.ch/browse/SLF4J-328
|
|
|
|
|
if (!isAndroid()) {
|
|
|
|
|
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
|
|
|
|
|
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
|
|
|
|
|
}
|
|
|
|
|
// the next line does the binding
|
|
|
|
|
StaticLoggerBinder.getSingleton();
|
|
|
|
|
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
|
|
|
|
|
reportActualBinding(staticLoggerBinderPathSet);
|
|
|
|
|
fixSubstituteLoggers();
|
|
|
|
|
replayEvents();
|
|
|
|
|
// release all resources in SUBST_FACTORY
|
|
|
|
|
SUBST_FACTORY.clear();
|
|
|
|
|
} catch (NoClassDefFoundError ncde) {
|
|
|
|
|
String msg = ncde.getMessage();
|
|
|
|
|
if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
|
|
|
|
|
INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
|
|
|
|
|
Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
|
|
|
|
|
Util.report("Defaulting to no-operation (NOP) logger implementation");
|
|
|
|
|
Util.report("See " + NO_STATICLOGGERBINDER_URL
|
|
|
|
|
+ " for further details.");
|
|
|
|
|
} else {
|
|
|
|
|
failedBinding(ncde);
|
|
|
|
|
throw ncde;
|
|
|
|
|
}
|
|
|
|
|
} catch (java.lang.NoSuchMethodError nsme) {
|
|
|
|
|
String msg = nsme.getMessage();
|
|
|
|
|
if (msg != null
|
|
|
|
|
&& msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
|
|
|
|
|
INITIALIZATION_STATE = FAILED_INITIALIZATION;
|
|
|
|
|
Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
|
|
|
|
|
Util.report("Your binding is version 1.5.5 or earlier.");
|
|
|
|
|
Util.report("Upgrade your binding to version 1.6.x.");
|
|
|
|
|
}
|
|
|
|
|
throw nsme;
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
failedBinding(e);
|
|
|
|
|
throw new IllegalStateException(
|
|
|
|
|
"Unexpected initialization failure", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private final static void bind() {
|
|
|
|
|
try {
|
|
|
|
|
Set<URL> staticLoggerBinderPathSet = null;
|
|
|
|
|
// skip check under android, see also
|
|
|
|
|
// http://jira.qos.ch/browse/SLF4J-328
|
|
|
|
|
if (!isAndroid()) {
|
|
|
|
|
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
|
|
|
|
|
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
|
|
|
|
|
}
|
|
|
|
|
// the next line does the binding
|
|
|
|
|
StaticLoggerBinder.getSingleton();
|
|
|
|
|
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
|
|
|
|
|
reportActualBinding(staticLoggerBinderPathSet);
|
|
|
|
|
fixSubstituteLoggers();
|
|
|
|
|
replayEvents();
|
|
|
|
|
// release all resources in SUBST_FACTORY
|
|
|
|
|
SUBST_FACTORY.clear();
|
|
|
|
|
} catch (NoClassDefFoundError ncde) {
|
|
|
|
|
String msg = ncde.getMessage();
|
|
|
|
|
if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
|
|
|
|
|
INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
|
|
|
|
|
Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
|
|
|
|
|
Util.report("Defaulting to no-operation (NOP) logger implementation");
|
|
|
|
|
Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details.");
|
|
|
|
|
} else {
|
|
|
|
|
failedBinding(ncde);
|
|
|
|
|
throw ncde;
|
|
|
|
|
}
|
|
|
|
|
} catch (java.lang.NoSuchMethodError nsme) {
|
|
|
|
|
String msg = nsme.getMessage();
|
|
|
|
|
if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
|
|
|
|
|
INITIALIZATION_STATE = FAILED_INITIALIZATION;
|
|
|
|
|
Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
|
|
|
|
|
Util.report("Your binding is version 1.5.5 or earlier.");
|
|
|
|
|
Util.report("Upgrade your binding to version 1.6.x.");
|
|
|
|
|
}
|
|
|
|
|
throw nsme;
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
failedBinding(e);
|
|
|
|
|
throw new IllegalStateException("Unexpected initialization failure", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void fixSubstituteLoggers() {
|
|
|
|
|
synchronized (SUBST_FACTORY) {
|
|
|
|
|
SUBST_FACTORY.postInitialization();
|
|
|
|
|
for (SubstituteLogger substLogger : SUBST_FACTORY.getLoggers()) {
|
|
|
|
|
Logger logger = getLogger(substLogger.getName());
|
|
|
|
|
substLogger.setDelegate(logger);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private static void fixSubstituteLoggers() {
|
|
|
|
|
synchronized (SUBST_FACTORY) {
|
|
|
|
|
SUBST_FACTORY.postInitialization();
|
|
|
|
|
for (SubstituteLogger substLogger : SUBST_FACTORY.getLoggers()) {
|
|
|
|
|
Logger logger = getLogger(substLogger.getName());
|
|
|
|
|
substLogger.setDelegate(logger);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void failedBinding(Throwable t) {
|
|
|
|
|
INITIALIZATION_STATE = FAILED_INITIALIZATION;
|
|
|
|
|
Util.report("Failed to instantiate SLF4J LoggerFactory", t);
|
|
|
|
|
}
|
|
|
|
|
static void failedBinding(Throwable t) {
|
|
|
|
|
INITIALIZATION_STATE = FAILED_INITIALIZATION;
|
|
|
|
|
Util.report("Failed to instantiate SLF4J LoggerFactory", t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void replayEvents() {
|
|
|
|
|
final LinkedBlockingQueue<SubstituteLoggingEvent> queue = SUBST_FACTORY
|
|
|
|
|
.getEventQueue();
|
|
|
|
|
final int queueSize = queue.size();
|
|
|
|
|
int count = 0;
|
|
|
|
|
final int maxDrain = 128;
|
|
|
|
|
List<SubstituteLoggingEvent> eventList = new ArrayList<SubstituteLoggingEvent>(
|
|
|
|
|
maxDrain);
|
|
|
|
|
while (true) {
|
|
|
|
|
int numDrained = queue.drainTo(eventList, maxDrain);
|
|
|
|
|
if (numDrained == 0)
|
|
|
|
|
break;
|
|
|
|
|
for (SubstituteLoggingEvent event : eventList) {
|
|
|
|
|
replaySingleEvent(event);
|
|
|
|
|
if (count++ == 0)
|
|
|
|
|
emitReplayOrSubstituionWarning(event, queueSize);
|
|
|
|
|
}
|
|
|
|
|
eventList.clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private static void replayEvents() {
|
|
|
|
|
final LinkedBlockingQueue<SubstituteLoggingEvent> queue = SUBST_FACTORY.getEventQueue();
|
|
|
|
|
final int queueSize = queue.size();
|
|
|
|
|
int count = 0;
|
|
|
|
|
final int maxDrain = 128;
|
|
|
|
|
List<SubstituteLoggingEvent> eventList = new ArrayList<SubstituteLoggingEvent>(maxDrain);
|
|
|
|
|
while (true) {
|
|
|
|
|
int numDrained = queue.drainTo(eventList, maxDrain);
|
|
|
|
|
if (numDrained == 0)
|
|
|
|
|
break;
|
|
|
|
|
for (SubstituteLoggingEvent event : eventList) {
|
|
|
|
|
replaySingleEvent(event);
|
|
|
|
|
if (count++ == 0)
|
|
|
|
|
emitReplayOrSubstituionWarning(event, queueSize);
|
|
|
|
|
}
|
|
|
|
|
eventList.clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void emitReplayOrSubstituionWarning(
|
|
|
|
|
SubstituteLoggingEvent event, int queueSize) {
|
|
|
|
|
if (event.getLogger().isDelegateEventAware()) {
|
|
|
|
|
emitReplayWarning(queueSize);
|
|
|
|
|
} else if (event.getLogger().isDelegateNOP()) {
|
|
|
|
|
// nothing to do
|
|
|
|
|
} else {
|
|
|
|
|
emitSubstitutionWarning();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private static void emitReplayOrSubstituionWarning(SubstituteLoggingEvent event, int queueSize) {
|
|
|
|
|
if (event.getLogger().isDelegateEventAware()) {
|
|
|
|
|
emitReplayWarning(queueSize);
|
|
|
|
|
} else if (event.getLogger().isDelegateNOP()) {
|
|
|
|
|
// nothing to do
|
|
|
|
|
} else {
|
|
|
|
|
emitSubstitutionWarning();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void replaySingleEvent(SubstituteLoggingEvent event) {
|
|
|
|
|
if (event == null)
|
|
|
|
|
return;
|
|
|
|
|
private static void replaySingleEvent(SubstituteLoggingEvent event) {
|
|
|
|
|
if (event == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
SubstituteLogger substLogger = event.getLogger();
|
|
|
|
|
String loggerName = substLogger.getName();
|
|
|
|
|
if (substLogger.isDelegateNull()) {
|
|
|
|
|
throw new IllegalStateException(
|
|
|
|
|
"Delegate logger cannot be null at this state.");
|
|
|
|
|
}
|
|
|
|
|
SubstituteLogger substLogger = event.getLogger();
|
|
|
|
|
String loggerName = substLogger.getName();
|
|
|
|
|
if (substLogger.isDelegateNull()) {
|
|
|
|
|
throw new IllegalStateException("Delegate logger cannot be null at this state.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (substLogger.isDelegateNOP()) {
|
|
|
|
|
// nothing to do
|
|
|
|
|
} else if (substLogger.isDelegateEventAware()) {
|
|
|
|
|
substLogger.log(event);
|
|
|
|
|
} else {
|
|
|
|
|
Util.report(loggerName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (substLogger.isDelegateNOP()) {
|
|
|
|
|
// nothing to do
|
|
|
|
|
} else if (substLogger.isDelegateEventAware()) {
|
|
|
|
|
substLogger.log(event);
|
|
|
|
|
} else {
|
|
|
|
|
Util.report(loggerName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void emitSubstitutionWarning() {
|
|
|
|
|
Util.report("The following set of substitute loggers may have been accessed");
|
|
|
|
|
Util.report("during the initialization phase. Logging calls during this");
|
|
|
|
|
Util.report("phase were not honored. However, subsequent logging calls to these");
|
|
|
|
|
Util.report("loggers will work as normally expected.");
|
|
|
|
|
Util.report("See also " + SUBSTITUTE_LOGGER_URL);
|
|
|
|
|
}
|
|
|
|
|
private static void emitSubstitutionWarning() {
|
|
|
|
|
Util.report("The following set of substitute loggers may have been accessed");
|
|
|
|
|
Util.report("during the initialization phase. Logging calls during this");
|
|
|
|
|
Util.report("phase were not honored. However, subsequent logging calls to these");
|
|
|
|
|
Util.report("loggers will work as normally expected.");
|
|
|
|
|
Util.report("See also " + SUBSTITUTE_LOGGER_URL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void emitReplayWarning(int eventCount) {
|
|
|
|
|
Util.report("A number ("
|
|
|
|
|
+ eventCount
|
|
|
|
|
+ ") of logging calls during the initialization phase have been intercepted and are");
|
|
|
|
|
Util.report("now being replayed. These are subject to the filtering rules of the underlying logging system.");
|
|
|
|
|
Util.report("See also " + REPLAY_URL);
|
|
|
|
|
}
|
|
|
|
|
private static void emitReplayWarning(int eventCount) {
|
|
|
|
|
Util.report("A number (" + eventCount + ") of logging calls during the initialization phase have been intercepted and are");
|
|
|
|
|
Util.report("now being replayed. These are subject to the filtering rules of the underlying logging system.");
|
|
|
|
|
Util.report("See also " + REPLAY_URL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private final static void versionSanityCheck() {
|
|
|
|
|
try {
|
|
|
|
|
String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
|
|
|
|
|
private final static void versionSanityCheck() {
|
|
|
|
|
try {
|
|
|
|
|
String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
|
|
|
|
|
|
|
|
|
|
boolean match = false;
|
|
|
|
|
for (String aAPI_COMPATIBILITY_LIST : API_COMPATIBILITY_LIST) {
|
|
|
|
|
if (requested.startsWith(aAPI_COMPATIBILITY_LIST)) {
|
|
|
|
|
match = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!match) {
|
|
|
|
|
Util.report("The requested version " + requested
|
|
|
|
|
+ " by your slf4j binding is not compatible with "
|
|
|
|
|
+ Arrays.asList(API_COMPATIBILITY_LIST).toString());
|
|
|
|
|
Util.report("See " + VERSION_MISMATCH + " for further details.");
|
|
|
|
|
}
|
|
|
|
|
} catch (java.lang.NoSuchFieldError nsfe) {
|
|
|
|
|
// given our large user base and 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) {
|
|
|
|
|
// we should never reach here
|
|
|
|
|
Util.report(
|
|
|
|
|
"Unexpected problem occured during version sanity check", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
boolean match = false;
|
|
|
|
|
for (String aAPI_COMPATIBILITY_LIST : API_COMPATIBILITY_LIST) {
|
|
|
|
|
if (requested.startsWith(aAPI_COMPATIBILITY_LIST)) {
|
|
|
|
|
match = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!match) {
|
|
|
|
|
Util.report("The requested version " + requested + " by your slf4j binding is not compatible with "
|
|
|
|
|
+ Arrays.asList(API_COMPATIBILITY_LIST).toString());
|
|
|
|
|
Util.report("See " + VERSION_MISMATCH + " for further details.");
|
|
|
|
|
}
|
|
|
|
|
} catch (java.lang.NoSuchFieldError nsfe) {
|
|
|
|
|
// given our large user base and 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) {
|
|
|
|
|
// we should never reach here
|
|
|
|
|
Util.report("Unexpected problem occured during version sanity check", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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";
|
|
|
|
|
// 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<URL> findPossibleStaticLoggerBinderPathSet() {
|
|
|
|
|
// use Set instead of list in order to deal with bug #138
|
|
|
|
|
// LinkedHashSet appropriate here because it preserves insertion order
|
|
|
|
|
// during iteration
|
|
|
|
|
Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
|
|
|
|
|
try {
|
|
|
|
|
ClassLoader loggerFactoryClassLoader = LoggerFactory.class
|
|
|
|
|
.getClassLoader();
|
|
|
|
|
Enumeration<URL> 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;
|
|
|
|
|
}
|
|
|
|
|
static Set<URL> findPossibleStaticLoggerBinderPathSet() {
|
|
|
|
|
// use Set instead of list in order to deal with bug #138
|
|
|
|
|
// LinkedHashSet appropriate here because it preserves insertion order
|
|
|
|
|
// during iteration
|
|
|
|
|
Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
|
|
|
|
|
try {
|
|
|
|
|
ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
|
|
|
|
|
Enumeration<URL> 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 boolean isAmbiguousStaticLoggerBinderPathSet(
|
|
|
|
|
Set<URL> binderPathSet) {
|
|
|
|
|
return binderPathSet.size() > 1;
|
|
|
|
|
}
|
|
|
|
|
private static boolean isAmbiguousStaticLoggerBinderPathSet(Set<URL> binderPathSet) {
|
|
|
|
|
return binderPathSet.size() > 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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(Set<URL> binderPathSet) {
|
|
|
|
|
if (isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
|
|
|
|
|
Util.report("Class path contains multiple SLF4J bindings.");
|
|
|
|
|
for (URL path : binderPathSet) {
|
|
|
|
|
Util.report("Found binding in [" + path + "]");
|
|
|
|
|
}
|
|
|
|
|
Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 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(Set<URL> binderPathSet) {
|
|
|
|
|
if (isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
|
|
|
|
|
Util.report("Class path contains multiple SLF4J bindings.");
|
|
|
|
|
for (URL path : binderPathSet) {
|
|
|
|
|
Util.report("Found binding in [" + path + "]");
|
|
|
|
|
}
|
|
|
|
|
Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static boolean isAndroid() {
|
|
|
|
|
String vendor = Util.safeGetSystemProperty(JAVA_VENDOR_PROPERTY);
|
|
|
|
|
if (vendor == null)
|
|
|
|
|
return false;
|
|
|
|
|
return vendor.toLowerCase().contains("android");
|
|
|
|
|
}
|
|
|
|
|
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(Set<URL> binderPathSet) {
|
|
|
|
|
// binderPathSet can be null under Android
|
|
|
|
|
if (binderPathSet != null
|
|
|
|
|
&& isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
|
|
|
|
|
Util.report("Actual binding is of type ["
|
|
|
|
|
+ StaticLoggerBinder.getSingleton()
|
|
|
|
|
.getLoggerFactoryClassStr() + "]");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private static void reportActualBinding(Set<URL> binderPathSet) {
|
|
|
|
|
// binderPathSet can be null under Android
|
|
|
|
|
if (binderPathSet != null && isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
|
|
|
|
|
Util.report("Actual binding is of type [" + StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr() + "]");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
|
|
|
|
public static Logger getLogger(String name) {
|
|
|
|
|
ILoggerFactory iLoggerFactory = getILoggerFactory();
|
|
|
|
|
return iLoggerFactory.getLogger(name);
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
|
|
|
|
public static Logger getLogger(String name) {
|
|
|
|
|
ILoggerFactory iLoggerFactory = getILoggerFactory();
|
|
|
|
|
return iLoggerFactory.getLogger(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return a logger named corresponding to the class passed as parameter,
|
|
|
|
|
* using the statically bound {@link ILoggerFactory} instance.
|
|
|
|
|
*
|
|
|
|
|
* <p>
|
|
|
|
|
* In case the 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>
|
|
|
|
|
*/
|
|
|
|
|
public static Logger getLogger(Class<?> clazz) {
|
|
|
|
|
Logger logger = getLogger(clazz.getName());
|
|
|
|
|
if (DETECT_LOGGER_NAME_MISMATCH) {
|
|
|
|
|
Class<?> autoComputedCallingClass = Util.getCallingClass();
|
|
|
|
|
if (autoComputedCallingClass != null
|
|
|
|
|
&& nonMatchingClasses(clazz, autoComputedCallingClass)) {
|
|
|
|
|
Util.report(String
|
|
|
|
|
.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".",
|
|
|
|
|
logger.getName(),
|
|
|
|
|
autoComputedCallingClass.getName()));
|
|
|
|
|
Util.report("See " + LOGGER_NAME_MISMATCH_URL
|
|
|
|
|
+ " for an explanation");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return logger;
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* Return a logger named corresponding to the class passed as parameter,
|
|
|
|
|
* using the statically bound {@link ILoggerFactory} instance.
|
|
|
|
|
*
|
|
|
|
|
* <p>
|
|
|
|
|
* In case the 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>
|
|
|
|
|
*/
|
|
|
|
|
public static Logger getLogger(Class<?> clazz) {
|
|
|
|
|
Logger logger = getLogger(clazz.getName());
|
|
|
|
|
if (DETECT_LOGGER_NAME_MISMATCH) {
|
|
|
|
|
Class<?> autoComputedCallingClass = Util.getCallingClass();
|
|
|
|
|
if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
|
|
|
|
|
Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),
|
|
|
|
|
autoComputedCallingClass.getName()));
|
|
|
|
|
Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return logger;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static boolean nonMatchingClasses(Class<?> clazz,
|
|
|
|
|
Class<?> autoComputedCallingClass) {
|
|
|
|
|
return !autoComputedCallingClass.isAssignableFrom(clazz);
|
|
|
|
|
}
|
|
|
|
|
private static boolean nonMatchingClasses(Class<?> clazz, Class<?> autoComputedCallingClass) {
|
|
|
|
|
return !autoComputedCallingClass.isAssignableFrom(clazz);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the {@link ILoggerFactory} instance in use.
|
|
|
|
|
* <p/>
|
|
|
|
|
* <p/>
|
|
|
|
|
* ILoggerFactory instance is bound with this class at compile time.
|
|
|
|
|
*
|
|
|
|
|
* @return the ILoggerFactory instance in use
|
|
|
|
|
*/
|
|
|
|
|
public static ILoggerFactory getILoggerFactory() {
|
|
|
|
|
if (INITIALIZATION_STATE == UNINITIALIZED) {
|
|
|
|
|
synchronized (LoggerFactory.class) {
|
|
|
|
|
if (INITIALIZATION_STATE == UNINITIALIZED) {
|
|
|
|
|
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
|
|
|
|
|
performInitialization();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
switch (INITIALIZATION_STATE) {
|
|
|
|
|
case SUCCESSFUL_INITIALIZATION:
|
|
|
|
|
return StaticLoggerBinder.getSingleton().getLoggerFactory();
|
|
|
|
|
case NOP_FALLBACK_INITIALIZATION:
|
|
|
|
|
return NOP_FALLBACK_FACTORY;
|
|
|
|
|
case FAILED_INITIALIZATION:
|
|
|
|
|
throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
|
|
|
|
|
case ONGOING_INITIALIZATION:
|
|
|
|
|
// support re-entrant behavior.
|
|
|
|
|
// See also http://jira.qos.ch/browse/SLF4J-97
|
|
|
|
|
return SUBST_FACTORY;
|
|
|
|
|
}
|
|
|
|
|
throw new IllegalStateException("Unreachable code");
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* Return the {@link ILoggerFactory} instance in use.
|
|
|
|
|
* <p/>
|
|
|
|
|
* <p/>
|
|
|
|
|
* ILoggerFactory instance is bound with this class at compile time.
|
|
|
|
|
*
|
|
|
|
|
* @return the ILoggerFactory instance in use
|
|
|
|
|
*/
|
|
|
|
|
public static ILoggerFactory getILoggerFactory() {
|
|
|
|
|
if (INITIALIZATION_STATE == UNINITIALIZED) {
|
|
|
|
|
synchronized (LoggerFactory.class) {
|
|
|
|
|
if (INITIALIZATION_STATE == UNINITIALIZED) {
|
|
|
|
|
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
|
|
|
|
|
performInitialization();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
switch (INITIALIZATION_STATE) {
|
|
|
|
|
case SUCCESSFUL_INITIALIZATION:
|
|
|
|
|
return StaticLoggerBinder.getSingleton().getLoggerFactory();
|
|
|
|
|
case NOP_FALLBACK_INITIALIZATION:
|
|
|
|
|
return NOP_FALLBACK_FACTORY;
|
|
|
|
|
case FAILED_INITIALIZATION:
|
|
|
|
|
throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
|
|
|
|
|
case ONGOING_INITIALIZATION:
|
|
|
|
|
// support re-entrant behavior.
|
|
|
|
|
// See also http://jira.qos.ch/browse/SLF4J-97
|
|
|
|
|
return SUBST_FACTORY;
|
|
|
|
|
}
|
|
|
|
|
throw new IllegalStateException("Unreachable code");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|