You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2022/03/31 05:49:56 UTC

[logging-log4j2] 06/07: Make LoggerContextFactory init lazier

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

mattsicker pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 922aa12b2eab33349de9a814acc0a5df5a65f0a7
Author: Matt Sicker <ma...@apache.org>
AuthorDate: Wed Mar 30 21:55:41 2022 -0500

    Make LoggerContextFactory init lazier
    
    Signed-off-by: Matt Sicker <ma...@apache.org>
---
 .../java/org/apache/logging/log4j/LogManager.java  | 124 ++++++++++-----------
 1 file changed, 61 insertions(+), 63 deletions(-)

diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java b/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java
index 887e1ed..c2d6b46 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java
@@ -16,11 +16,6 @@
  */
 package org.apache.logging.log4j;
 
-import java.net.URI;
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
 import org.apache.logging.log4j.internal.LogManagerStatus;
 import org.apache.logging.log4j.message.MessageFactory;
 import org.apache.logging.log4j.message.StringFormatterMessageFactory;
@@ -30,12 +25,18 @@ import org.apache.logging.log4j.spi.LoggerContextFactory;
 import org.apache.logging.log4j.spi.Provider;
 import org.apache.logging.log4j.spi.Terminable;
 import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.LazyValue;
 import org.apache.logging.log4j.util.LoaderUtil;
 import org.apache.logging.log4j.util.PropertiesUtil;
 import org.apache.logging.log4j.util.ProviderUtil;
 import org.apache.logging.log4j.util.StackLocatorUtil;
 import org.apache.logging.log4j.util.Strings;
 
+import java.net.URI;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
 /**
  * The anchor point for the Log4j logging system. The most common usage of this class is to obtain a named
  * {@link Logger}. The method {@link #getLogger()} is provided as the most convenient way to obtain a named Logger based
@@ -63,19 +64,17 @@ public class LogManager {
     // for convenience
     private static final String FQCN = LogManager.class.getName();
 
-    private static volatile LoggerContextFactory factory;
-
     /**
      * Scans the classpath to find all logging implementation. Currently, only one will be used but this could be
      * extended to allow multiple implementations to be used.
      */
-    static {
+    private static final LazyValue<LoggerContextFactory> PROVIDER = LazyValue.from(() -> {
         // Shortcut binding to force a specific logging implementation.
         final PropertiesUtil managerProps = PropertiesUtil.getProperties();
         final String factoryClassName = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
         if (factoryClassName != null) {
             try {
-                factory = LoaderUtil.newCheckedInstanceOf(factoryClassName, LoggerContextFactory.class);
+                return LoaderUtil.newCheckedInstanceOf(factoryClassName, LoggerContextFactory.class);
             } catch (final ClassNotFoundException cnfe) {
                 LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClassName);
             } catch (final Exception ex) {
@@ -83,48 +82,48 @@ public class LogManager {
             }
         }
 
-        if (factory == null) {
-            final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<>();
-            // note that the following initial call to ProviderUtil may block until a Provider has been installed when
-            // running in an OSGi environment
-            if (ProviderUtil.hasProviders()) {
-                for (final Provider provider : ProviderUtil.getProviders()) {
-                    final Class<? extends LoggerContextFactory> factoryClass = provider.loadLoggerContextFactory();
-                    if (factoryClass != null) {
-                        try {
-                            factories.put(provider.getPriority(), factoryClass.newInstance());
-                        } catch (final Exception e) {
-                            LOGGER.error("Unable to create class {} specified in provider URL {}", factoryClass.getName(), provider
-                                    .getUrl(), e);
-                        }
-                    }
-                }
-
-                if (factories.isEmpty()) {
-                    LOGGER.error("Log4j2 could not find a logging implementation. "
-                            + "Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
-                    factory = SimpleLoggerContextFactory.INSTANCE;
-                } else if (factories.size() == 1) {
-                    factory = factories.get(factories.lastKey());
-                } else {
-                    final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n");
-                    for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) {
-                        sb.append("Factory: ").append(entry.getValue().getClass().getName());
-                        sb.append(", Weighting: ").append(entry.getKey()).append('\n');
-                    }
-                    factory = factories.get(factories.lastKey());
-                    sb.append("Using factory: ").append(factory.getClass().getName());
-                    LOGGER.warn(sb.toString());
+        // note that the following initial call to ProviderUtil may block until a Provider has been installed when
+        // running in an OSGi environment
+        if (!ProviderUtil.hasProviders()) {
+            LOGGER.error("Log4j2 could not find a logging implementation. "
+                    + "Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
+            return SimpleLoggerContextFactory.INSTANCE;
+        }
 
+        final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<>();
+        for (final Provider provider : ProviderUtil.getProviders()) {
+            final Class<? extends LoggerContextFactory> factoryClass = provider.loadLoggerContextFactory();
+            if (factoryClass != null) {
+                try {
+                    factories.put(provider.getPriority(), factoryClass.newInstance());
+                } catch (final Exception e) {
+                    LOGGER.error("Unable to create class {} specified in provider URL {}", factoryClass.getName(), provider
+                            .getUrl(), e);
                 }
-            } else {
-                LOGGER.error("Log4j2 could not find a logging implementation. "
-                        + "Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
-                factory = SimpleLoggerContextFactory.INSTANCE;
             }
         }
+
+        if (factories.isEmpty()) {
+            LOGGER.error("Log4j2 could not find a logging implementation. "
+                    + "Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
+            return SimpleLoggerContextFactory.INSTANCE;
+        } else if (factories.size() == 1) {
+            return factories.get(factories.lastKey());
+        } else {
+            final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n");
+            for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) {
+                sb.append("Factory: ").append(entry.getValue().getClass().getName());
+                sb.append(", Weighting: ").append(entry.getKey()).append('\n');
+            }
+            final var factory = factories.get(factories.lastKey());
+            sb.append("Using factory: ").append(factory.getClass().getName());
+            LOGGER.warn(sb.toString());
+            return factory;
+        }
+    }).map(factory -> {
         LogManagerStatus.setInitialized(true);
-    }
+        return factory;
+    });
 
     /**
      * Prevents instantiation
@@ -154,7 +153,7 @@ public class LogManager {
      */
     public static LoggerContext getContext() {
         try {
-            return factory.getContext(FQCN, null, null, true);
+            return getFactory().getContext(FQCN, null, null, true);
         } catch (final IllegalStateException ex) {
             LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
             return SimpleLoggerContextFactory.INSTANCE.getContext(FQCN, null, null, true);
@@ -173,7 +172,7 @@ public class LogManager {
     public static LoggerContext getContext(final boolean currentContext) {
         // TODO: would it be a terrible idea to try and find the caller ClassLoader here?
         try {
-            return factory.getContext(FQCN, null, null, currentContext, null, null);
+            return getFactory().getContext(FQCN, null, null, currentContext, null, null);
         } catch (final IllegalStateException ex) {
             LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
             return SimpleLoggerContextFactory.INSTANCE.getContext(FQCN, null, null, currentContext, null, null);
@@ -193,7 +192,7 @@ public class LogManager {
      */
     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) {
         try {
-            return factory.getContext(FQCN, loader, null, currentContext);
+            return getFactory().getContext(FQCN, loader, null, currentContext);
         } catch (final IllegalStateException ex) {
             LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
             return SimpleLoggerContextFactory.INSTANCE.getContext(FQCN, loader, null, currentContext);
@@ -215,7 +214,7 @@ public class LogManager {
     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
             final Object externalContext) {
         try {
-            return factory.getContext(FQCN, loader, externalContext, currentContext);
+            return getFactory().getContext(FQCN, loader, externalContext, currentContext);
         } catch (final IllegalStateException ex) {
             LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
             return SimpleLoggerContextFactory.INSTANCE.getContext(FQCN, loader, externalContext, currentContext);
@@ -237,7 +236,7 @@ public class LogManager {
     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
             final URI configLocation) {
         try {
-            return factory.getContext(FQCN, loader, null, currentContext, configLocation, null);
+            return getFactory().getContext(FQCN, loader, null, currentContext, configLocation, null);
         } catch (final IllegalStateException ex) {
             LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
             return SimpleLoggerContextFactory.INSTANCE.getContext(FQCN, loader, null, currentContext, configLocation,
@@ -261,7 +260,7 @@ public class LogManager {
     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
             final Object externalContext, final URI configLocation) {
         try {
-            return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, null);
+            return getFactory().getContext(FQCN, loader, externalContext, currentContext, configLocation, null);
         } catch (final IllegalStateException ex) {
             LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
             return SimpleLoggerContextFactory.INSTANCE.getContext(FQCN, loader, externalContext, currentContext,
@@ -286,7 +285,7 @@ public class LogManager {
     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
             final Object externalContext, final URI configLocation, final String name) {
         try {
-            return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, name);
+            return getFactory().getContext(FQCN, loader, externalContext, currentContext, configLocation, name);
         } catch (final IllegalStateException ex) {
             LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
             return SimpleLoggerContextFactory.INSTANCE.getContext(FQCN, loader, externalContext, currentContext,
@@ -306,7 +305,7 @@ public class LogManager {
      */
     protected static LoggerContext getContext(final String fqcn, final boolean currentContext) {
         try {
-            return factory.getContext(fqcn, null, null, currentContext);
+            return getFactory().getContext(fqcn, null, null, currentContext);
         } catch (final IllegalStateException ex) {
             LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
             return SimpleLoggerContextFactory.INSTANCE.getContext(fqcn, null, null, currentContext);
@@ -328,7 +327,7 @@ public class LogManager {
     protected static LoggerContext getContext(final String fqcn, final ClassLoader loader,
             final boolean currentContext) {
         try {
-            return factory.getContext(fqcn, loader, null, currentContext);
+            return getFactory().getContext(fqcn, loader, null, currentContext);
         } catch (final IllegalStateException ex) {
             LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
             return SimpleLoggerContextFactory.INSTANCE.getContext(fqcn, loader, null, currentContext);
@@ -353,7 +352,7 @@ public class LogManager {
     protected static LoggerContext getContext(final String fqcn, final ClassLoader loader,
                                               final boolean currentContext, final URI configLocation, final String name) {
         try {
-            return factory.getContext(fqcn, loader, null, currentContext, configLocation, name);
+            return getFactory().getContext(fqcn, loader, null, currentContext, configLocation, name);
         } catch (final IllegalStateException ex) {
             LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
             return SimpleLoggerContextFactory.INSTANCE.getContext(fqcn, loader, null, currentContext);
@@ -389,7 +388,7 @@ public class LogManager {
      * @since 2.6
      */
     public static void shutdown(final boolean currentContext) {
-        factory.shutdown(FQCN, null, currentContext, false);
+        getFactory().shutdown(FQCN, null, currentContext, false);
     }
 
     /**
@@ -409,7 +408,7 @@ public class LogManager {
      * @since 2.13.0
      */
     public static void shutdown(final boolean currentContext, final boolean allContexts) {
-        factory.shutdown(FQCN, null, currentContext, allContexts);
+        getFactory().shutdown(FQCN, null, currentContext, allContexts);
     }
 
     /**
@@ -422,7 +421,7 @@ public class LogManager {
      * @since 2.6
      */
     public static void shutdown(final LoggerContext context) {
-        if (context != null && context instanceof Terminable) {
+        if (context instanceof Terminable) {
             ((Terminable) context).terminate();
         }
     }
@@ -433,7 +432,7 @@ public class LogManager {
      * @return The LoggerContextFactory.
      */
     public static LoggerContextFactory getFactory() {
-        return factory;
+        return PROVIDER.get();
     }
 
     /**
@@ -449,9 +448,8 @@ public class LogManager {
      *
      * @param factory the LoggerContextFactory to use.
      */
-    // FIXME: should we allow only one update of the factory?
     public static void setFactory(final LoggerContextFactory factory) {
-        LogManager.factory = factory;
+        PROVIDER.set(factory);
     }
 
     /**
@@ -687,7 +685,7 @@ public class LogManager {
      * @return The Logger.
      */
     protected static Logger getLogger(final String fqcn, final String name) {
-        return factory.getContext(fqcn, null, null, false).getLogger(name);
+        return getFactory().getContext(fqcn, null, null, false).getLogger(name);
     }
 
     /**