diff --git a/src/java/org/apache/commons/logging/impl/LogFactoryImpl.java b/src/java/org/apache/commons/logging/impl/LogFactoryImpl.java
index d0487e1..1d1f558 100644
--- a/src/java/org/apache/commons/logging/impl/LogFactoryImpl.java
+++ b/src/java/org/apache/commons/logging/impl/LogFactoryImpl.java
@@ -76,6 +76,8 @@ public class LogFactoryImpl extends LogFactory {
*/
public LogFactoryImpl() {
super();
+ initDiagnostics(); // method on this object
+ logDiagnostic("Instance created.");
}
@@ -108,6 +110,11 @@ public class LogFactoryImpl extends LogFactory {
// ----------------------------------------------------- Instance Variables
+ /**
+ * The string prefixed to every message output by the logDiagnostic method.
+ */
+ private String diagnosticPrefix;
+
/**
* Configuration attributes.
*/
@@ -160,7 +167,6 @@ public class LogFactoryImpl extends LogFactory {
// --------------------------------------------------------- Public Methods
-
/**
* Return the configuration attribute with the specified name (if any),
* or null if there is no such attribute.
@@ -250,6 +256,7 @@ public class LogFactoryImpl extends LogFactory {
*/
public void release() {
+ logDiagnostic("Releasing all known loggers");
instances.clear();
}
@@ -287,13 +294,90 @@ public class LogFactoryImpl extends LogFactory {
}
+ // ------------------------------------------------------
+ // Static Methods
+ //
+ // These methods only defined as workarounds for a java 1.2 bug;
+ // theoretically none of these are needed.
+ // ------------------------------------------------------
+
+ /**
+ * Gets the context classloader.
+ * This method is a workaround for a java 1.2 compiler bug.
+ */
+ protected static ClassLoader getContextClassLoader() throws LogConfigurationException {
+ return LogFactory.getContextClassLoader();
+ }
+
+ /**
+ * Workaround for bug in Java1.2; in theory this method is not needed.
+ * See LogFactory.isInternalLoggingEnabled.
+ */
+ protected static boolean isDiagnosticsEnabled() {
+ return LogFactory.isDiagnosticsEnabled();
+ }
+
+ /**
+ * Workaround for bug in Java1.2; in theory this method is not needed.
+ * See LogFactory.getClassLoader.
+ */
+ protected static ClassLoader getClassLoader(Class clazz) {
+ return LogFactory.getClassLoader(clazz);
+ }
+
// ------------------------------------------------------ Protected Methods
+ /**
+ * Calculate and cache a string that uniquely identifies this instance,
+ * including which classloader the object was loaded from.
+ *
+ * This string will later be prefixed to each "internal logging" message + * emitted, so that users can clearly see any unexpected behaviour. + *
+ * Note that this method does not detect whether internal logging is + * enabled or not, nor where to output stuff if it is; that is all + * handled by the parent LogFactory class. This method just computes + * its own unique prefix for log messages. + */ + private void initDiagnostics() { + // It would be nice to include an identifier of the context classloader + // that this LogFactoryImpl object is responsible for. However that + // isn't possible as that information isn't available. It is possible + // to figure this out by looking at the logging from LogFactory to + // see the context & impl ids from when this object was instantiated, + // in order to link the impl id output as this object's prefix back to + // the context it is intended to manage. + Class clazz = this.getClass(); + ClassLoader classLoader = getClassLoader(clazz); + diagnosticPrefix = clazz.getName() + "@" + classLoader.toString() + ":"; + } + /** + * Output a diagnostic message to a user-specified destination (if the + * user has enabled diagnostic logging). + * + * @param msg + */ + protected void logDiagnostic(String msg) { + if (isDiagnosticsEnabled()) { + logRawDiagnostic(diagnosticPrefix + msg); + } + } /** * Return the fully qualified Java classname of the {@link Log} * implementation we will be using. + *
+ * This method looks in the following places: + *
+ * There are two possible reasons why we successfully loaded the + * specified log adapter class then failed to cast it to a Log object: + *
+ * Here we try to figure out which case has occurred so we can give the + * user some reasonable feedback. + * + * @param logInterface is the class that this LogFactoryImpl class needs + * to return the adapter as. + * @param logClass is the adapter class we successfully loaded (but which + * could not be cast to type logInterface). */ private LogConfigurationException reportInvalidLogAdapter( Class logInterface, Class logClass) { @@ -421,6 +552,21 @@ public class LogFactoryImpl extends LogFactory { Class interfaces[] = logClass.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { if (LOG_INTERFACE.equals(interfaces[i].getName())) { + + if (isDiagnosticsEnabled()) { + ClassLoader logInterfaceClassLoader = getClassLoader(logInterface); + ClassLoader logAdapterClassLoader = getClassLoader(logClass); + Class logAdapterInterface = interfaces[i]; + ClassLoader logAdapterInterfaceClassLoader = getClassLoader(logAdapterInterface); + logDiagnostic( + "Class " + logClassName + " was found in classloader " + + objectId(logAdapterClassLoader) + + " but it implements the Log interface as loaded" + + " from classloader " + objectId(logAdapterInterfaceClassLoader) + + " not the one loaded by this class's classloader " + + objectId(logInterfaceClassLoader)); + } + throw new LogConfigurationException ("Invalid class loader hierarchy. " + "You have more than one version of '" + @@ -433,16 +579,7 @@ public class LogFactoryImpl extends LogFactory { ("Class " + logClassName + " does not implement '" + LOG_INTERFACE + "'."); } - - /** - * Gets the context classloader. - * This method is a workaround for a java 1.2 compiler bug. - */ - protected static ClassLoader getContextClassLoader() throws LogConfigurationException { - return LogFactory.getContextClassLoader(); - } - - + /** * MUST KEEP THIS METHOD PRIVATE. * @@ -453,7 +590,10 @@ public class LogFactoryImpl extends LogFactory { *
* * Load a class, try first the thread class loader, and - * if it fails use the loader that loaded this class. + * if it fails use the loader that loaded this class. Actually, as + * the thread (context) classloader should always be the same as or a + * child of the classloader that loaded this class, the fallback should + * never be used. */ private static Class loadClass( final String name ) throws ClassNotFoundException @@ -489,12 +629,17 @@ public class LogFactoryImpl extends LogFactory { */ protected boolean isJdk13LumberjackAvailable() { + // note: the algorithm here is different from isLog4JAvailable. + // I think isLog4JAvailable is correct....see bugzilla#31597 + logDiagnostic("Checking for Jdk13Lumberjack."); try { loadClass("java.util.logging.Logger"); loadClass("org.apache.commons.logging.impl.Jdk13LumberjackLogger"); - return (true); + logDiagnostic("Found Jdk13Lumberjack."); + return true; } catch (Throwable t) { - return (false); + logDiagnostic("Did not find Jdk13Lumberjack."); + return false; } } @@ -508,6 +653,9 @@ public class LogFactoryImpl extends LogFactory { */ protected boolean isJdk14Available() { + // note: the algorithm here is different from isLog4JAvailable. + // I think isLog4JAvailable is correct.... + logDiagnostic("Checking for Jdk14."); try { loadClass("java.util.logging.Logger"); loadClass("org.apache.commons.logging.impl.Jdk14Logger"); @@ -515,9 +663,11 @@ public class LogFactoryImpl extends LogFactory { if (throwable.getDeclaredMethod("getStackTrace", null) == null) { return (false); } - return (true); + logDiagnostic("Found Jdk14."); + return true; } catch (Throwable t) { - return (false); + logDiagnostic("Did not find Jdk14."); + return false; } } @@ -527,12 +677,16 @@ public class LogFactoryImpl extends LogFactory { */ protected boolean isLog4JAvailable() { + logDiagnostic("Checking for Log4J"); try { - loadClass("org.apache.commons.logging.impl.Log4JLogger").getClassLoader() - .loadClass("org.apache.log4j.Logger" ); - return (true); + Class adapterClass = loadClass("org.apache.commons.logging.impl.Log4JLogger"); + ClassLoader cl = getClassLoader(adapterClass); + Class loggerClass = cl.loadClass("org.apache.log4j.Logger" ); + logDiagnostic("Found Log4J"); + return true; } catch (Throwable t) { - return (false); + logDiagnostic("Did not find Log4J"); + return false; } }