You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by gg...@apache.org on 2022/01/09 21:26:15 UTC

[logging-log4j2] 01/02: Refactor 1.2 bridge internals to use a single private log manager and adapter.

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit f64791b10cbcea8a8cf3ccbad924c74071f0211c
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun Jan 9 16:26:07 2022 -0500

    Refactor 1.2 bridge internals to use a single private log manager and
    adapter.
    
    - Implement LogManager.getCurrentLoggers().
    - The class loader found in the bridge and passed down instead of a
    FQCN.
    - Adds a package private extension to LoggerRepository called
    LoggerRepository2 to allow class loader propagation.
    - Core availability is computed once in LogManager.
    - Move Log4j to the top of POM dependencies
    - Split commit 1/2 for cherry-picking to master.
---
 .../java/org/apache/log4j/BasicConfigurator.java   |  14 +-
 .../src/main/java/org/apache/log4j/Category.java   | 176 ++++-----------
 .../src/main/java/org/apache/log4j/Hierarchy.java  | 133 +++++++-----
 .../src/main/java/org/apache/log4j/LogManager.java | 237 ++++++++-------------
 .../src/main/java/org/apache/log4j/Logger.java     |  26 ++-
 .../java/org/apache/log4j/LoggerRepository2.java   |  31 +++
 .../main/java/org/apache/log4j/spi/RootLogger.java |   3 +-
 .../test/java/org/apache/log4j/CategoryTest.java   |   4 +-
 .../test/java/org/apache/log4j/LogManagerTest.java |  12 +-
 9 files changed, 283 insertions(+), 353 deletions(-)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/BasicConfigurator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/BasicConfigurator.java
index 2b7ec7f..da90e2c 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/BasicConfigurator.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/BasicConfigurator.java
@@ -18,12 +18,11 @@ package org.apache.log4j;
 
 /**
  * Provided for compatibility with Log4j 1.x.
+ *
+ * @since 0.8.1
  */
 public class BasicConfigurator {
 
-    protected BasicConfigurator() {
-    }
-
     public static void configure() {
         LogManager.reconfigure();
     }
@@ -36,10 +35,13 @@ public class BasicConfigurator {
         // no-op
     }
 
+    public static void resetConfiguration() {
+        LogManager.resetConfiguration();
+    }
+
     /**
-     * No-op implementation.
+     * Constructs a new instance.
      */
-    public static void resetConfiguration() {
-        // no-op
+    protected BasicConfigurator() {
     }
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java
index 7d0cb11..286ae32 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java
@@ -19,8 +19,6 @@ package org.apache.log4j;
 import java.util.Enumeration;
 import java.util.Map;
 import java.util.ResourceBundle;
-import java.util.WeakHashMap;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
 import org.apache.log4j.helpers.NullEnumeration;
@@ -28,18 +26,16 @@ import org.apache.log4j.legacy.core.CategoryUtil;
 import org.apache.log4j.or.ObjectRenderer;
 import org.apache.log4j.or.RendererMap;
 import org.apache.log4j.spi.AppenderAttachable;
-import org.apache.log4j.spi.LoggerFactory;
 import org.apache.log4j.spi.LoggerRepository;
 import org.apache.log4j.spi.LoggingEvent;
-import org.apache.log4j.spi.RendererSupport;
 import org.apache.logging.log4j.message.LocalizedMessage;
 import org.apache.logging.log4j.message.MapMessage;
 import org.apache.logging.log4j.message.Message;
 import org.apache.logging.log4j.message.ObjectMessage;
 import org.apache.logging.log4j.message.SimpleMessage;
-import org.apache.logging.log4j.spi.AbstractLoggerAdapter;
 import org.apache.logging.log4j.spi.ExtendedLogger;
 import org.apache.logging.log4j.spi.LoggerContext;
+import org.apache.logging.log4j.util.StackLocatorUtil;
 import org.apache.logging.log4j.util.Strings;
 
 /**
@@ -47,26 +43,8 @@ import org.apache.logging.log4j.util.Strings;
  */
 public class Category implements AppenderAttachable {
 
-    private static PrivateAdapter adapter = new PrivateAdapter();
-
-    private static final Map<LoggerContext, ConcurrentMap<String, Logger>> CONTEXT_MAP =
-        new WeakHashMap<>();
-
     private static final String FQCN = Category.class.getName();
 
-    private static final boolean isCoreAvailable;
-    
-    static {
-        boolean available;
-
-        try {
-            available = Class.forName("org.apache.logging.log4j.core.Logger") != null;
-        } catch (Exception ex) {
-            available = false;
-        }
-        isCoreAvailable = available;
-    }
-
     /**
      * The name of this category.
      */
@@ -79,14 +57,14 @@ public class Category implements AppenderAttachable {
      * to <code>false</code> too. See the user manual for more details.
      */
     protected boolean additive = true;
-    
+
     /**
      * The assigned level of this category. The <code>level</code> variable need not be assigned a value in which case it is
      * inherited form the hierarchy.
      */
     volatile protected Level level;
 
-    private final RendererMap rendererMap;
+    private RendererMap rendererMap;
 
     /**
      * The parent of this category. All categories have at least one ancestor which is the root category.
@@ -112,7 +90,7 @@ public class Category implements AppenderAttachable {
         this.name = name;
         this.logger = context.getLogger(name);
         this.repository = LogManager.getLoggerRepository();
-        this.rendererMap = ((RendererSupport) repository).getRendererMap();
+        //this.rendererMap = ((RendererSupport) repository).getRendererMap();
     }
 
     /**
@@ -120,50 +98,22 @@ public class Category implements AppenderAttachable {
      * @param name The name of the Logger.
      */
     protected Category(final String name) {
-        this(PrivateManager.getContext(), name);
+        this(Hierarchy.getContext(), name);
     }
 
-    private Category(final org.apache.logging.log4j.Logger logger) {
+    Category(final org.apache.logging.log4j.Logger logger) {
         this.logger = logger;
-        rendererMap = ((RendererSupport) LogManager.getLoggerRepository()).getRendererMap();
+        //rendererMap = ((RendererSupport) LogManager.getLoggerRepository()).getRendererMap();
     }
 
     public static Category getInstance(final String name) {
-        return getInstance(PrivateManager.getContext(), name, adapter);
-    }
-
-    static Logger getInstance(final LoggerContext context, final String name) {
-        return getInstance(context, name, adapter);
-    }
-
-    static Logger getInstance(final LoggerContext context, final String name, final LoggerFactory factory) {
-        final ConcurrentMap<String, Logger> loggers = getLoggersMap(context);
-        Logger logger = loggers.get(name);
-        if (logger != null) {
-            return logger;
-        }
-        logger = factory.makeNewLoggerInstance(name);
-        final Logger prev = loggers.putIfAbsent(name, logger);
-        return prev == null ? logger : prev;
-    }
-
-    static Logger getInstance(final LoggerContext context, final String name, final PrivateAdapter factory) {
-        final ConcurrentMap<String, Logger> loggers = getLoggersMap(context);
-        Logger logger = loggers.get(name);
-        if (logger != null) {
-            return logger;
-        }
-        logger = factory.newLogger(name, context);
-        final Logger prev = loggers.putIfAbsent(name, logger);
-        return prev == null ? logger : prev;
+        // Depth 2 gets the call site of this method.
+        return LogManager.getLogger(name, StackLocatorUtil.getCallerClassLoader(2));
     }
 
     public static Category getInstance(@SuppressWarnings("rawtypes") final Class clazz) {
-        return getInstance(clazz.getName());
-    }
-
-    static Logger getInstance(final LoggerContext context, @SuppressWarnings("rawtypes") final Class clazz) {
-        return getInstance(context, clazz.getName());
+        // Depth 2 gets the call site of this method.
+        return LogManager.getLogger(clazz.getName(), StackLocatorUtil.getCallerClassLoader(2));
     }
 
     public final String getName() {
@@ -175,15 +125,15 @@ public class Category implements AppenderAttachable {
     }
 
     public final Category getParent() {
-        if (!isCoreAvailable) {
+        if (!LogManager.isLog4jCorePresent()) {
             return null;
         }
-        org.apache.logging.log4j.Logger parent = CategoryUtil.getParent(logger);
-        LoggerContext loggerContext = CategoryUtil.getLoggerContext(logger);
+        final org.apache.logging.log4j.Logger parent = CategoryUtil.getParent(logger);
+        final LoggerContext loggerContext = CategoryUtil.getLoggerContext(logger);
         if (parent == null || loggerContext == null) {
             return null;
         }
-        final ConcurrentMap<String, Logger> loggers = getLoggersMap(loggerContext);
+        final ConcurrentMap<String, Logger> loggers = Hierarchy.getLoggersMap(loggerContext);
         final Logger parentLogger = loggers.get(parent.getName());
         return parentLogger == null ? new Category(parent) : parentLogger;
     }
@@ -192,38 +142,23 @@ public class Category implements AppenderAttachable {
         return getInstance(Strings.EMPTY);
     }
 
-    static Logger getRoot(final LoggerContext context) {
-        return getInstance(context, Strings.EMPTY);
-    }
-
-    private static ConcurrentMap<String, Logger> getLoggersMap(final LoggerContext context) {
-        synchronized (CONTEXT_MAP) {
-            ConcurrentMap<String, Logger> map = CONTEXT_MAP.get(context);
-            if (map == null) {
-                map = new ConcurrentHashMap<>();
-                CONTEXT_MAP.put(context, map);
-            }
-            return map;
-        }
-    }
-
     /**
      * Returns all the currently defined categories in the default hierarchy as an
      * {@link java.util.Enumeration Enumeration}.
-     * 
+     *
      * <p>
      * The root category is <em>not</em> included in the returned
      * {@link Enumeration}.
      * </p>
-     * 
+     *
      * @return and Enumeration of the Categories.
-     * 
+     *
      * @deprecated Please use {@link LogManager#getCurrentLoggers()} instead.
      */
     @SuppressWarnings("rawtypes")
     @Deprecated
     public static Enumeration getCurrentCategories() {
-        return LogManager.getCurrentLoggers();
+        return LogManager.getCurrentLoggers(StackLocatorUtil.getCallerClassLoader(2));
     }
 
     /**
@@ -262,7 +197,7 @@ public class Category implements AppenderAttachable {
 
     /**
      * Gets the the {@link LoggerRepository} where this <code>Category</code> instance is attached.
-     * 
+     *
      * @deprecated Please use {@link #getLoggerRepository()} instead.
      * @since 1.1
      */
@@ -273,7 +208,7 @@ public class Category implements AppenderAttachable {
 
     /**
      * Gets the the {@link LoggerRepository} where this <code>Category</code> is attached.
-     * 
+     *
      * @since 1.2
      */
     public LoggerRepository getLoggerRepository() {
@@ -305,7 +240,7 @@ public class Category implements AppenderAttachable {
     }
 
     private void setLevel(final String levelStr) {
-        if (isCoreAvailable) {
+        if (LogManager.isLog4jCorePresent()) {
             CategoryUtil.setLevel(logger, org.apache.logging.log4j.Level.toLevel(levelStr));
         }
     }
@@ -396,10 +331,10 @@ public class Category implements AppenderAttachable {
      * @since 1.0
      */
     synchronized void closeNestedAppenders() {
-        Enumeration enumeration = this.getAllAppenders();
+        final Enumeration enumeration = this.getAllAppenders();
         if (enumeration != null) {
             while (enumeration.hasMoreElements()) {
-                Appender a = (Appender) enumeration.nextElement();
+                final Appender a = (Appender) enumeration.nextElement();
                 if (a instanceof AppenderAttachable) {
                     a.close();
                 }
@@ -466,11 +401,12 @@ public class Category implements AppenderAttachable {
         final org.apache.logging.log4j.Level lvl = org.apache.logging.log4j.Level.toLevel(level.toString());
         if (logger instanceof ExtendedLogger) {
             @SuppressWarnings("unchecked")
+            final
             Message msg = message instanceof Message ? (Message) message : message instanceof Map ?
                     new MapMessage((Map) message) : new ObjectMessage(message);
             ((ExtendedLogger) logger).logMessage(fqcn, lvl, null, msg, t);
         } else {
-            ObjectRenderer renderer = get(message.getClass());
+            final ObjectRenderer renderer = get(message.getClass());
             final Message msg = message instanceof Message ? (Message) message : renderer != null ?
                     new RenderedMessage(renderer, message) : new ObjectMessage(message);
             logger.log(lvl, msg, t);
@@ -479,24 +415,24 @@ public class Category implements AppenderAttachable {
 
     /**
      * Tests if the named category exists (in the default hierarchy).
-     * 
+     *
      * @param name The name to test.
      * @return Whether the name exists.
-     * 
+     *
      * @deprecated Please use {@link LogManager#exists(String)} instead.
      * @since 0.8.5
      */
     @Deprecated
-    public static boolean exists(final String name) {
-        return PrivateManager.getContext().hasLogger(name);
+    public static Logger exists(final String name) {
+        return LogManager.exists(name);
     }
 
     public boolean getAdditivity() {
-        return isCoreAvailable ? CategoryUtil.isAdditive(logger) : false;
+        return LogManager.isLog4jCorePresent() ? CategoryUtil.isAdditive(logger) : false;
     }
 
     public void setAdditivity(final boolean additivity) {
-        if (isCoreAvailable) {
+        if (LogManager.isLog4jCorePresent()) {
             CategoryUtil.setAdditivity(logger, additivity);
         }
     }
@@ -504,7 +440,7 @@ public class Category implements AppenderAttachable {
     /**
      * Only the Hiearchy class can set the hiearchy of a category. Default package access is MANDATORY here.
      */
-    final void setHierarchy(LoggerRepository repository) {
+    final void setHierarchy(final LoggerRepository repository) {
         this.repository = repository;
     }
 
@@ -517,10 +453,10 @@ public class Category implements AppenderAttachable {
             return bundle;
         }
         String name = logger.getName();
-        if (isCoreAvailable) {
-            LoggerContext ctx = CategoryUtil.getLoggerContext(logger);
+        if (LogManager.isLog4jCorePresent()) {
+            final LoggerContext ctx = CategoryUtil.getLoggerContext(logger);
             if (ctx != null) {
-                final ConcurrentMap<String, Logger> loggers = getLoggersMap(ctx);
+                final ConcurrentMap<String, Logger> loggers = Hierarchy.getLoggersMap(ctx);
                 while ((name = getSubName(name)) != null) {
                     final Logger subLogger = loggers.get(name);
                     if (subLogger != null) {
@@ -546,7 +482,7 @@ public class Category implements AppenderAttachable {
     /**
      * If <code>assertion</code> parameter is {@code false}, then logs
      * <code>msg</code> as an {@link #error(Object) error} statement.
-     * 
+     *
      * <p>
      * The <code>assert</code> method has been renamed to <code>assertLog</code>
      * because <code>assert</code> is a language reserved word in JDK 1.4.
@@ -554,7 +490,7 @@ public class Category implements AppenderAttachable {
      *
      * @param assertion The assertion.
      * @param msg       The message to print if <code>assertion</code> is false.
-     * 
+     *
      * @since 1.2
      */
     public void assertLog(final boolean assertion, final String msg) {
@@ -629,39 +565,11 @@ public class Category implements AppenderAttachable {
         }
     }
 
-    private static class PrivateAdapter extends AbstractLoggerAdapter<Logger> {
-
-        @Override
-        protected Logger newLogger(final String name, final org.apache.logging.log4j.spi.LoggerContext context) {
-            return new Logger(context, name);
-        }
-
-        @Override
-        protected org.apache.logging.log4j.spi.LoggerContext getContext() {
-            return PrivateManager.getContext();
-        }
-    }
-
-    /**
-     * Private LogManager.
-     */
-    private static class PrivateManager extends org.apache.logging.log4j.LogManager {
-        private static final String FQCN = Category.class.getName();
-
-        public static LoggerContext getContext() {
-            return getContext(FQCN, false);
-        }
-
-        public static org.apache.logging.log4j.Logger getLogger(final String name) {
-            return getLogger(FQCN, name);
-        }
-    }
-
     private boolean isEnabledFor(final org.apache.logging.log4j.Level level) {
         return logger.isEnabled(level);
     }
 
-    private <T> ObjectRenderer get(Class<T> clazz) {
+    private <T> ObjectRenderer get(final Class<T> clazz) {
         ObjectRenderer renderer = null;
         for (Class<? super T> c = clazz; c != null; c = c.getSuperclass()) {
             renderer = rendererMap.get(c);
@@ -676,13 +584,13 @@ public class Category implements AppenderAttachable {
         return null;
     }
 
-    ObjectRenderer searchInterfaces(Class<?> c) {
+    ObjectRenderer searchInterfaces(final Class<?> c) {
         ObjectRenderer renderer = rendererMap.get(c);
         if (renderer != null) {
             return renderer;
         }
-        Class<?>[] ia = c.getInterfaces();
-        for (Class<?> clazz : ia) {
+        final Class<?>[] ia = c.getInterfaces();
+        for (final Class<?> clazz : ia) {
             renderer = searchInterfaces(clazz);
             if (renderer != null) {
                 return renderer;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Hierarchy.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Hierarchy.java
index b497706..a7a059a 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/Hierarchy.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Hierarchy.java
@@ -24,16 +24,22 @@ package org.apache.log4j;
 import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.Vector;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
 import org.apache.log4j.helpers.LogLog;
 import org.apache.log4j.or.ObjectRenderer;
 import org.apache.log4j.or.RendererMap;
 import org.apache.log4j.spi.HierarchyEventListener;
 import org.apache.log4j.spi.LoggerFactory;
-import org.apache.log4j.spi.LoggerRepository;
 import org.apache.log4j.spi.RendererSupport;
 import org.apache.log4j.spi.ThrowableRenderer;
 import org.apache.log4j.spi.ThrowableRendererSupport;
+import org.apache.logging.log4j.core.appender.AsyncAppender;
+import org.apache.logging.log4j.spi.AbstractLoggerAdapter;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.apache.logging.log4j.util.Strings;
 
 /**
  * This class is specialized in retrieving loggers by name and also maintaining the logger hierarchy.
@@ -52,18 +58,77 @@ import org.apache.log4j.spi.ThrowableRendererSupport;
  * provision node.
  * </p>
  */
-public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRendererSupport {
+public class Hierarchy implements LoggerRepository2, RendererSupport, ThrowableRendererSupport {
+
+    private static class PrivateLoggerAdapter extends AbstractLoggerAdapter<Logger> {
+
+        @Override
+        protected org.apache.logging.log4j.spi.LoggerContext getContext() {
+            return PrivateLogManager.getContext();
+        }
+
+        @Override
+        protected Logger newLogger(final String name, final org.apache.logging.log4j.spi.LoggerContext context) {
+            return new Logger(context, name);
+        }
+    }
+
+    /**
+     * Private LogManager.
+     */
+    private static class PrivateLogManager extends org.apache.logging.log4j.LogManager {
+        private static final String FQCN = Hierarchy.class.getName();
+
+        public static LoggerContext getContext() {
+            return getContext(FQCN, false);
+        }
+
+        public static org.apache.logging.log4j.Logger getLogger(final String name) {
+            return getLogger(FQCN, name);
+        }
+    }
+
+    private static final PrivateLoggerAdapter LOGGER_ADAPTER = new PrivateLoggerAdapter();
+
+    private static final WeakHashMap<LoggerContext, ConcurrentMap<String, Logger>> CONTEXT_MAP = new WeakHashMap<>();
+
+    static LoggerContext getContext() {
+        return PrivateLogManager.getContext();
+    }
+
+    static Logger getInstance(final LoggerContext context, final String name) {
+        return getInstance(context, name, LOGGER_ADAPTER);
+    }
+
+    static Logger getInstance(final LoggerContext context, final String name, final LoggerFactory factory) {
+        return getLoggersMap(context).computeIfAbsent(name, k -> factory.makeNewLoggerInstance(name));
+    }
+
+    static Logger getInstance(final LoggerContext context, final String name, final PrivateLoggerAdapter factory) {
+        return getLoggersMap(context).computeIfAbsent(name, k -> factory.newLogger(name, context));
+    }
+
+    static ConcurrentMap<String, Logger> getLoggersMap(final LoggerContext context) {
+        synchronized (CONTEXT_MAP) {
+            return CONTEXT_MAP.computeIfAbsent(context, k -> new ConcurrentHashMap<>());
+        }
+    }
+
+    static Logger getRootLogger(final LoggerContext context) {
+        return getInstance(context, org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME);
+    }
 
     private final LoggerFactory defaultFactory;
     private final Vector listeners;
-
     Hashtable ht;
     Logger root;
     RendererMap rendererMap;
     int thresholdInt;
     Level threshold;
     boolean emittedNoAppenderWarning;
+
     boolean emittedNoResourceBundleWarning;
+
     private ThrowableRenderer throwableRenderer;
 
     /**
@@ -132,12 +197,11 @@ public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRe
      */
     @Override
     public Logger exists(final String name) {
-        final Object o = ht.get(new CategoryKey(name));
-        if (o instanceof Logger) {
-            return (Logger) o;
-        } else {
+        final LoggerContext ctx = getContext();
+        if (!ctx.hasLogger(name)) {
             return null;
         }
+        return Logger.getLogger(name);
     }
 
     @Override
@@ -207,18 +271,13 @@ public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRe
      */
     @Override
     public Logger getLogger(final String name) {
-        return getLogger(name, defaultFactory);
+        return getInstance(getContext(), name);
     }
 
-    /**
-     * Gets an integer representation of the this repository's threshold.
-     *
-     * @since 1.2
-     */
-    // public
-    // int getThresholdInt() {
-    // return thresholdInt;
-    // }
+    @Override
+    public Logger getLogger(final String name, final ClassLoader classLoader) {
+        return getInstance(LogManager.getContext(classLoader), name);
+    }
 
     /**
      * Gets a new logger instance named as the first parameter using <code>factory</code>.
@@ -233,36 +292,12 @@ public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRe
      */
     @Override
     public Logger getLogger(final String name, final LoggerFactory factory) {
-        // System.out.println("getInstance("+name+") called.");
-        final CategoryKey key = new CategoryKey(name);
-        // Synchronize to prevent write conflicts. Read conflicts (in
-        // getChainedLevel method) are possible only if variable
-        // assignments are non-atomic.
-        Logger logger;
+        return getInstance(getContext(), name, factory);
+    }
 
-        synchronized (ht) {
-            final Object o = ht.get(key);
-            if (o == null) {
-                logger = factory.makeNewLoggerInstance(name);
-                logger.setHierarchy(this);
-                ht.put(key, logger);
-                updateParents(logger);
-                return logger;
-            } else if (o instanceof Logger) {
-                return (Logger) o;
-            } else if (o instanceof ProvisionNode) {
-                // System.out.println("("+name+") ht.get(this) returned ProvisionNode");
-                logger = factory.makeNewLoggerInstance(name);
-                logger.setHierarchy(this);
-                ht.put(key, logger);
-                updateChildren((ProvisionNode) o, logger);
-                updateParents(logger);
-                return logger;
-            } else {
-                // It should be impossible to arrive here
-                return null; // but let's keep the compiler happy.
-            }
-        }
+    @Override
+    public Logger getLogger(final String name, final LoggerFactory factory, final ClassLoader classLoader) {
+        return getInstance(LogManager.getContext(classLoader), name, factory);
     }
 
     /**
@@ -280,7 +315,7 @@ public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRe
      */
     @Override
     public Logger getRootLogger() {
-        return root;
+        return getInstance(getContext(), org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME);
     }
 
     /**
@@ -336,7 +371,7 @@ public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRe
     @Override
     public void resetConfiguration() {
 
-        getRootLogger().setLevel((Level) Level.DEBUG);
+        getRootLogger().setLevel(Level.DEBUG);
         root.setResourceBundle(null);
         setThreshold(Level.ALL);
 
@@ -393,7 +428,7 @@ public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRe
      */
     @Override
     public void setThreshold(final String levelStr) {
-        final Level l = (Level) Level.toLevel(levelStr, null);
+        final Level l = Level.toLevel(levelStr, null);
         if (l != null) {
             setThreshold(l);
         } else {
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java b/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java
index 3f92cdf..46bff05 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java
@@ -16,22 +16,22 @@
  */
 package org.apache.log4j;
 
+import java.util.Collections;
 import java.util.Enumeration;
+import java.util.stream.Collectors;
 
-import org.apache.log4j.helpers.NullEnumeration;
 import org.apache.log4j.legacy.core.ContextUtil;
-import org.apache.log4j.or.ObjectRenderer;
-import org.apache.log4j.or.RendererMap;
-import org.apache.log4j.spi.HierarchyEventListener;
+import org.apache.log4j.spi.DefaultRepositorySelector;
 import org.apache.log4j.spi.LoggerFactory;
 import org.apache.log4j.spi.LoggerRepository;
-import org.apache.log4j.spi.RendererSupport;
+import org.apache.log4j.spi.NOPLoggerRepository;
 import org.apache.log4j.spi.RepositorySelector;
+import org.apache.log4j.spi.RootLogger;
 import org.apache.logging.log4j.spi.LoggerContext;
-import org.apache.logging.log4j.util.Strings;
+import org.apache.logging.log4j.util.StackLocatorUtil;
 
 /**
- *
+ * The main entry point to Log4j 1.
  */
 public final class LogManager {
 
@@ -61,188 +61,133 @@ public final class LogManager {
 
     static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";
 
-    private static final LoggerRepository REPOSITORY = new Repository();
+    static private RepositorySelector repositorySelector;
 
-    private static final boolean isLog4jCore;
+    private static final boolean LOG4J_CORE_PRESENT;
 
     static {
-        boolean core = false;
+        LOG4J_CORE_PRESENT = checkLog4jCore();
+        //
+        // By default we use a DefaultRepositorySelector which always returns 'hierarchy'.
+        Hierarchy hierarchy = new Hierarchy(new RootLogger(Level.DEBUG));
+        repositorySelector = new DefaultRepositorySelector(hierarchy);
+    }
+
+    private static boolean checkLog4jCore() {
         try {
-            if (Class.forName("org.apache.logging.log4j.core.LoggerContext") != null) {
-                core = true;
-            }
+            return Class.forName("org.apache.logging.log4j.core.LoggerContext") != null;
         } catch (Exception ex) {
-            // Ignore the exception;
+            return false;
         }
-        isLog4jCore = core;
     }
 
-    public static Logger getRootLogger() {
-        return Category.getInstance(PrivateManager.getContext(), Strings.EMPTY);
+    public static Logger exists(final String name) {
+        return getLoggerRepository().exists(name);
     }
 
-    public static Logger getLogger(final String name) {
-        return Category.getInstance(PrivateManager.getContext(), name);
+    /**
+     * Gets a LoggerContext.
+     *
+     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
+     *        ClassLoader.
+     * @return a LoggerContext.
+     */
+    static LoggerContext getContext(final ClassLoader classLoader) {
+        return org.apache.logging.log4j.LogManager.getContext(classLoader, false);
+    }
+
+    /**
+     * Gets an enumeration of the current loggers.
+     * 
+     * @return an enumeration of the current loggers.
+     */
+    @SuppressWarnings("rawtypes")
+    public static Enumeration getCurrentLoggers() {
+        return getCurrentLoggers(StackLocatorUtil.getCallerClassLoader(2));
+    }
+
+    @SuppressWarnings("rawtypes")
+    static Enumeration getCurrentLoggers(final ClassLoader classLoader) {
+        // @formatter:off
+        return Collections.enumeration(
+            LogManager.getContext(classLoader).getLoggerRegistry()
+                .getLoggers().stream().map(e -> LogManager.getLogger(e.getName(), classLoader))
+                .collect(Collectors.toList()));
+        // @formatter:on
     }
 
     public static Logger getLogger(final Class<?> clazz) {
-        return Category.getInstance(PrivateManager.getContext(), clazz.getName());
+        // Depth 2 gets the call site of this method.
+        return getLoggerRepository2().getLogger(clazz.getName(), StackLocatorUtil.getCallerClassLoader(2));
+    }
+
+    public static Logger getLogger(final String name) {
+        // Depth 2 gets the call site of this method.
+        return getLoggerRepository2().getLogger(name, StackLocatorUtil.getCallerClassLoader(2));
+    }
+
+    static Logger getLogger(final String name, final ClassLoader classLoader) {
+        return getLoggerRepository2().getLogger(name, classLoader);
     }
 
     public static Logger getLogger(final String name, final LoggerFactory factory) {
-        return Category.getInstance(PrivateManager.getContext(), name);
+        // Depth 2 gets the call site of this method.
+        return getLoggerRepository2().getLogger(name, factory, StackLocatorUtil.getCallerClassLoader(2));
     }
 
-    public static Logger exists(final String name) {
-        final LoggerContext ctx = PrivateManager.getContext();
-        if (!ctx.hasLogger(name)) {
-            return null;
+    static Logger getLogger(final String name, final LoggerFactory factory, final ClassLoader classLoader) {
+        return getLoggerRepository2().getLogger(name, factory, classLoader);
+    }
+
+    public static LoggerRepository getLoggerRepository() {
+        if (repositorySelector == null) {
+            repositorySelector = new DefaultRepositorySelector(new NOPLoggerRepository());
         }
-        return Logger.getLogger(name);
+        return repositorySelector.getLoggerRepository();
     }
 
-    @SuppressWarnings("rawtypes")
-    public static Enumeration getCurrentLoggers() {
-        return getLoggerRepository().getCurrentLoggers();
+    static LoggerRepository2 getLoggerRepository2() {
+        // TODO Hack
+        return (LoggerRepository2) getLoggerRepository();
     }
 
-    static void reconfigure() {
-        if (isLog4jCore) {
-            final LoggerContext ctx = PrivateManager.getContext();
-            ContextUtil.reconfigure(ctx);
-        }
+    public static Logger getRootLogger() {
+        return getLoggerRepository2().getRootLogger();
     }
 
-    /**
-     * No-op implementation.
-     */
-    public static void shutdown() {
+    static boolean isLog4jCorePresent() {
+        return LOG4J_CORE_PRESENT;
+    }
+
+    static void reconfigure() {
+        if (isLog4jCorePresent()) {
+            ContextUtil.reconfigure(Hierarchy.getContext());
+        }
     }
 
     /**
      * No-op implementation.
      */
     public static void resetConfiguration() {
+        // noop
     }
 
     /**
      * No-op implementation.
+     * 
      * @param selector The RepositorySelector.
      * @param guard prevents calls at the incorrect time.
      * @throws IllegalArgumentException if a parameter is invalid.
      */
-    public static void setRepositorySelector(final RepositorySelector selector, final Object guard)
-        throws IllegalArgumentException {
-    }
-
-    public static LoggerRepository getLoggerRepository() {
-        return REPOSITORY;
+    public static void setRepositorySelector(final RepositorySelector selector, final Object guard) throws IllegalArgumentException {
+        // noop
     }
 
     /**
-     * The Repository.
+     * No-op implementation.
      */
-    private static class Repository implements LoggerRepository, RendererSupport {
-
-        private final RendererMap rendererMap = new RendererMap();
-
-        @Override
-        public RendererMap getRendererMap() {
-            return rendererMap;
-        }
-
-        @Override
-        public void addHierarchyEventListener(final HierarchyEventListener listener) {
-
-        }
-
-        @Override
-        public boolean isDisabled(final int level) {
-            return false;
-        }
-
-        @Override
-        public void setThreshold(final Level level) {
-
-        }
-
-        @Override
-        public void setThreshold(final String val) {
-
-        }
-
-        @Override
-        public void emitNoAppenderWarning(final Category cat) {
-
-        }
-
-        @Override
-        public Level getThreshold() {
-            return Level.OFF;
-        }
-
-        @Override
-        public Logger getLogger(final String name) {
-            return Category.getInstance(PrivateManager.getContext(), name);
-        }
-
-        @Override
-        public Logger getLogger(final String name, final LoggerFactory factory) {
-            return Category.getInstance(PrivateManager.getContext(), name);
-        }
-
-        @Override
-        public Logger getRootLogger() {
-            return Category.getRoot(PrivateManager.getContext());
-        }
-
-        @Override
-        public Logger exists(final String name) {
-            return LogManager.exists(name);
-        }
-
-        @Override
-        public void shutdown() {
-        }
-
-        @Override
-        @SuppressWarnings("rawtypes")
-        public Enumeration getCurrentLoggers() {
-            return NullEnumeration.getInstance();
-        }
-
-        @Override
-        @SuppressWarnings("rawtypes")
-        public Enumeration getCurrentCategories() {
-            return NullEnumeration.getInstance();
-        }
-
-        @Override
-        public void fireAddAppenderEvent(final Category logger, final Appender appender) {
-        }
-
-        @Override
-        public void resetConfiguration() {
-        }
-
-        @Override
-        public void setRenderer(Class renderedClass, ObjectRenderer renderer) {
-            rendererMap.put(renderedClass, renderer);
-        }
+    public static void shutdown() {
+        // noop
     }
 
-    /**
-     * Internal LogManager.
-     */
-    private static class PrivateManager extends org.apache.logging.log4j.LogManager {
-        private static final String FQCN = LogManager.class.getName();
-
-        public static LoggerContext getContext() {
-            return getContext(FQCN, false);
-        }
-
-        public static org.apache.logging.log4j.Logger getLogger(final String name) {
-            return getLogger(FQCN, name);
-        }
-    }
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Logger.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Logger.java
index 674a0c1..19c523e 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/Logger.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Logger.java
@@ -18,6 +18,7 @@ package org.apache.log4j;
 
 import org.apache.log4j.spi.LoggerFactory;
 import org.apache.logging.log4j.spi.LoggerContext;
+import org.apache.logging.log4j.util.StackLocatorUtil;
 
 /**
  *
@@ -29,28 +30,31 @@ public class Logger extends Category {
      */
     private static final String FQCN = Logger.class.getName();
  
-    protected Logger(final String name) {
-        super(name);
-    }
-
-    Logger(final LoggerContext context, final String name) {
-        super(context, name);
+    public static Logger getLogger(final Class<?> clazz) {
+        // Depth 2 gets the call site of this method.
+        return LogManager.getLogger(clazz.getName(), StackLocatorUtil.getCallerClassLoader(2));
     }
 
     public static Logger getLogger(final String name) {
-        return LogManager.getLogger(name);
+        // Depth 2 gets the call site of this method.
+        return LogManager.getLogger(name, StackLocatorUtil.getCallerClassLoader(2)); 
     }
 
-    public static Logger getLogger(final Class<?> clazz) {
-        return LogManager.getLogger(clazz);
+    public static Logger getLogger(final String name, final LoggerFactory factory) {
+        // Depth 2 gets the call site of this method.
+        return LogManager.getLogger(name, factory, StackLocatorUtil.getCallerClassLoader(2));
     }
 
     public static Logger getRootLogger() {
         return LogManager.getRootLogger();
     }
 
-    public static Logger getLogger(final String name, final LoggerFactory factory) {
-        return LogManager.getLogger(name, factory);
+    Logger(final LoggerContext context, final String name) {
+        super(context, name);
+    }
+
+    protected Logger(final String name) {
+        super(name);
     }
 
     public boolean isTraceEnabled() {
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/LoggerRepository2.java b/log4j-1.2-api/src/main/java/org/apache/log4j/LoggerRepository2.java
new file mode 100644
index 0000000..30c88ee
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/LoggerRepository2.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j;
+
+import org.apache.log4j.spi.LoggerFactory;
+import org.apache.log4j.spi.LoggerRepository;
+
+/**
+ * A LoggerRepository that accounts for the caller's class loader.
+ */
+interface LoggerRepository2 extends LoggerRepository {
+
+    Logger getLogger(String name, ClassLoader classLoader);
+
+    Logger getLogger(String name, LoggerFactory factory, ClassLoader classLoader);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RootLogger.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RootLogger.java
index afd4619..79db491 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RootLogger.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RootLogger.java
@@ -34,7 +34,8 @@ public final class RootLogger extends Logger {
      * The root logger names itself as "root". However, the root logger cannot be retrieved by name.
      */
     public RootLogger(Level level) {
-        // Note that the Log4j 2 root logger name is "".
+        // The Log4j 1 root logger name is "root".
+        // The Log4j 2 root logger name is "".
         super("root");
         setLevel(level);
     }
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java
index a618136..a6e9510 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java
@@ -18,8 +18,8 @@
 package org.apache.log4j;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import java.lang.reflect.Method;
@@ -74,7 +74,7 @@ public class CategoryTest {
 
     @Test
     public void testExist() throws Exception {
-        assertFalse(Category.exists("Does not exist for sure"));
+        assertNull(Category.exists("Does not exist for sure"));
     }
 
     /**
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LogManagerTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LogManagerTest.java
index d8fcd16..b3d56b2 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/LogManagerTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LogManagerTest.java
@@ -24,21 +24,25 @@ import java.util.Enumeration;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import org.junit.Ignore;
 import org.junit.Test;
 
+/**
+ * Tests {@link LogManager}.
+ */
 public class LogManagerTest {
 
     private static final String SIMPLE_NAME = LogManagerTest.class.getSimpleName();
 
+    List<String> getCurrentLoggerNames() {
+        return Collections.list((Enumeration<Logger>) LogManager.getCurrentLoggers()).stream().map(Logger::getName).collect(Collectors.toList());
+    }
+
     @Test
-    @Ignore("WIP")
     public void testGetCurrentLoggers() {
         Logger.getLogger(SIMPLE_NAME);
         Logger.getLogger(SIMPLE_NAME + ".foo");
         Logger.getLogger(SIMPLE_NAME + ".foo.bar");
-        final List<String> names = Collections.list((Enumeration<Logger>) LogManager.getCurrentLoggers()).stream().map(Logger::getName)
-            .collect(Collectors.toList());
+        final List<String> names = getCurrentLoggerNames();
         assertTrue(names.contains(SIMPLE_NAME));
         assertTrue(names.contains(SIMPLE_NAME + ".foo"));
         assertTrue(names.contains(SIMPLE_NAME + ".foo.bar"));