You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2020/06/29 00:39:52 UTC

[logging-log4j2] branch master updated: LOG4J2-2867 - Obtain ContextDataProviders asynchronously

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

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


The following commit(s) were added to refs/heads/master by this push:
     new f14654a  LOG4J2-2867 - Obtain ContextDataProviders asynchronously
f14654a is described below

commit f14654af0bba3fc1382682f538e7ef1bea814c11
Author: Ralph Goers <rg...@apache.org>
AuthorDate: Sun Jun 28 17:39:33 2020 -0700

    LOG4J2-2867 - Obtain ContextDataProviders asynchronously
---
 .../apache/logging/log4j/core/LoggerContext.java   |  5 +-
 .../log4j/core/impl/ThreadContextDataInjector.java | 48 +++++++++++----
 .../org/apache/logging/log4j/core/LoggingTest.java | 69 ++++++++++++++++++++++
 3 files changed, 111 insertions(+), 11 deletions(-)

diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
index 8b181d0..0efea61 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
@@ -27,6 +27,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -43,6 +44,7 @@ import org.apache.logging.log4j.core.config.DefaultConfiguration;
 import org.apache.logging.log4j.core.config.NullConfiguration;
 import org.apache.logging.log4j.core.config.Reconfigurable;
 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.core.impl.ThreadContextDataInjector;
 import org.apache.logging.log4j.core.jmx.Server;
 import org.apache.logging.log4j.core.util.Cancellable;
 import org.apache.logging.log4j.core.util.ExecutorServices;
@@ -59,7 +61,6 @@ import org.apache.logging.log4j.spi.Terminable;
 import org.apache.logging.log4j.spi.ThreadContextMapFactory;
 import org.apache.logging.log4j.util.PropertiesUtil;
 
-
 /**
  * The LoggerContext is the anchor for the logging system. It maintains a list of all the loggers requested by
  * applications and a reference to the Configuration. The Configuration will contain the configured loggers, appenders,
@@ -136,6 +137,7 @@ public class LoggerContext extends AbstractLifeCycle
             externalMap.put(EXTERNAL_CONTEXT_KEY, externalContext);
         }
         this.configLocation = configLocn;
+        CompletableFuture.runAsync(ThreadContextDataInjector::initServiceProviders);
     }
 
     /**
@@ -164,6 +166,7 @@ public class LoggerContext extends AbstractLifeCycle
         } else {
             configLocation = null;
         }
+        CompletableFuture.runAsync(ThreadContextDataInjector::initServiceProviders);
     }
 
     public void addShutdownListener(LoggerContextShutdownAware listener) {
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
index 5ce7819..ace66ef 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
@@ -23,6 +23,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.ServiceLoader;
 import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.ThreadContext;
@@ -60,6 +62,39 @@ public class ThreadContextDataInjector {
     public static Collection<ContextDataProvider> contextDataProviders =
             new ConcurrentLinkedDeque<>();
 
+    private static volatile List<ContextDataProvider> serviceProviders = null;
+    private static final Lock providerLock = new ReentrantLock();
+
+    public static void initServiceProviders() {
+        if (serviceProviders == null) {
+            providerLock.lock();
+            try {
+                if (serviceProviders == null) {
+                    serviceProviders = getServiceProviders();
+                }
+            } finally {
+                providerLock.unlock();
+            }
+        }
+    }
+
+    private static List<ContextDataProvider> getServiceProviders() {
+        List<ContextDataProvider> providers = new ArrayList<>();
+        for (final ClassLoader classLoader : LoaderUtil.getClassLoaders()) {
+            try {
+                for (final ContextDataProvider provider : ServiceLoader.load(ContextDataProvider.class, classLoader)) {
+                    if (providers.stream().noneMatch((p) -> p.getClass().isAssignableFrom(provider.getClass()))) {
+                        providers.add(provider);
+                    }
+                }
+            } catch (final Throwable ex) {
+                LOGGER.debug("Unable to access Context Data Providers {}", ex.getMessage());
+            }
+        }
+        return providers;
+    }
+
+
     /**
      * Default {@code ContextDataInjector} for the legacy {@code Map<String, String>}-based ThreadContext (which is
      * also the ThreadContext implementation used for web applications).
@@ -248,17 +283,10 @@ public class ThreadContextDataInjector {
     }
 
     private static List<ContextDataProvider> getProviders() {
+        initServiceProviders();
         final List<ContextDataProvider> providers = new ArrayList<>(contextDataProviders);
-        for (final ClassLoader classLoader : LoaderUtil.getClassLoaders()) {
-            try {
-                for (final ContextDataProvider provider : ServiceLoader.load(ContextDataProvider.class, classLoader)) {
-                    if (providers.stream().noneMatch((p) -> p.getClass().isAssignableFrom(provider.getClass()))) {
-                        providers.add(provider);
-                    }
-                }
-            } catch (final Throwable ex) {
-                LOGGER.debug("Unable to access Context Data Providers {}", ex.getMessage());
-            }
+        if (serviceProviders != null) {
+            providers.addAll(serviceProviders);
         }
         return providers;
     }
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/LoggingTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/LoggingTest.java
new file mode 100644
index 0000000..1365a08
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/LoggingTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.logging.log4j.core;
+
+import java.util.List;
+
+import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.apache.logging.log4j.util.Timer;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+
+import static org.junit.Assert.assertEquals;
+
+
+public class LoggingTest {
+
+    private static final String CONFIG = "log4j-list.xml";
+
+    @Rule
+    public final TestName testName = new TestName();
+    private ListAppender list;
+
+
+    @Rule
+    public LoggerContextRule context = new LoggerContextRule(CONFIG);
+
+    private void assertEventCount(final List<LogEvent> events, final int expected) {
+        assertEquals("Incorrect number of events.", expected, events.size());
+    }
+
+    @Before
+    public void before() {
+        logger = context.getLogger("LoggerTest");
+    }
+
+    org.apache.logging.log4j.Logger logger;
+
+    @Test
+    public void logTime() {
+        Timer timer = new Timer("initial");
+        timer.start();
+        logger.info("This is a test");
+        System.out.println(timer.stop());
+        timer = new Timer("more", 100);
+/*        timer.start();
+        for (int i=0; i < 100; ++i) {
+            logger.info("This is another test");
+        }
+        System.out.println(timer.stop());*/
+    }
+}
+