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 2016/03/14 07:57:01 UTC

logging-log4j2 git commit: LOG4J2-1116 Add ThreadLocalRegistry

Repository: logging-log4j2
Updated Branches:
  refs/heads/LOG4J2-1116 [created] 53a4b152e


LOG4J2-1116 Add ThreadLocalRegistry


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/53a4b152
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/53a4b152
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/53a4b152

Branch: refs/heads/LOG4J2-1116
Commit: 53a4b152e1f747b5e1761c990a67f423b19295d5
Parents: 3542dae
Author: Ralph Goers <rg...@nextiva.com>
Authored: Sun Mar 13 23:56:27 2016 -0700
Committer: Ralph Goers <rg...@nextiva.com>
Committed: Sun Mar 13 23:56:27 2016 -0700

----------------------------------------------------------------------
 .../log4j/message/ReusableMessageFactory.java   |  55 ++++++----
 .../logging/log4j/spi/AbstractLogger.java       |  56 +++++++++-
 .../logging/log4j/util/ThreadLocalRegistry.java |  29 +++++
 .../log4j/util/ThreadLocalRegistryAware.java    |  24 ++++
 .../org/apache/logging/log4j/core/Logger.java   |  28 ++---
 .../logging/log4j/core/LoggerContext.java       |   7 ++
 .../core/util/DefaultThreadLocalRegistry.java   |  52 +++++++++
 .../log4j/core/util/Log4jThreadLocal.java       |  81 ++++++++++++++
 .../log4j/perf/jmh/StackTraceBenchmark.java     | 110 +++++++++++++++++++
 9 files changed, 403 insertions(+), 39 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/53a4b152/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableMessageFactory.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableMessageFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableMessageFactory.java
index 85f2dea..3ee71f4 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableMessageFactory.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableMessageFactory.java
@@ -19,6 +19,8 @@ package org.apache.logging.log4j.message;
 import java.io.Serializable;
 
 import org.apache.logging.log4j.util.PerformanceSensitive;
+import org.apache.logging.log4j.util.ThreadLocalRegistry;
+import org.apache.logging.log4j.util.ThreadLocalRegistryAware;
 
 /**
  * Implementation of the {@link MessageFactory} interface that avoids allocating temporary objects where possible.
@@ -30,26 +32,32 @@ import org.apache.logging.log4j.util.PerformanceSensitive;
  * @since 2.6
  */
 @PerformanceSensitive("allocation")
-public final class ReusableMessageFactory implements MessageFactory, Serializable {
-
-    /**
-     * Instance of ReusableMessageFactory..
-     */
-    public static final ReusableMessageFactory INSTANCE = new ReusableMessageFactory();
+public final class ReusableMessageFactory implements MessageFactory, Serializable, ThreadLocalRegistryAware {
 
     private static final long serialVersionUID = -8970940216592525651L;
-    private static ThreadLocal<ReusableParameterizedMessage> threadLocalParameterized = new ThreadLocal<>();
-    private static ThreadLocal<ReusableSimpleMessage> threadLocalSimpleMessage = new ThreadLocal<>();
-    private static ThreadLocal<ReusableObjectMessage> threadLocalObjectMessage = new ThreadLocal<>();
+    // @TODO - The ThreadLocals need to be restored on deserialization.
+    private transient ThreadLocal<ReusableParameterizedMessage> threadLocalParameterized = null;
+    private transient ThreadLocal<ReusableSimpleMessage> threadLocalSimpleMessage = null;
+    private transient ThreadLocal<ReusableObjectMessage> threadLocalObjectMessage = null;
 
     /**
      * Constructs a message factory.
+     * @param registry The ThreadLocalRegistry
      */
-    public ReusableMessageFactory() {
+    @SuppressWarnings("unchecked")
+    public ReusableMessageFactory(ThreadLocalRegistry registry) {
         super();
+        if (registry != null) {
+            threadLocalParameterized = (ThreadLocal<ReusableParameterizedMessage>) registry.get("ParameterizedMessage");
+            threadLocalSimpleMessage = (ThreadLocal<ReusableSimpleMessage>) registry.get("SimpleMessage");
+            threadLocalObjectMessage = (ThreadLocal<ReusableObjectMessage>) registry.get("ObjectMessage");
+        }
+    }
+
+    public ReusableMessageFactory() {
     }
 
-    private static ReusableParameterizedMessage getParameterized() {
+    private ReusableParameterizedMessage getParameterized() {
         ReusableParameterizedMessage result = threadLocalParameterized.get();
         if (result == null) {
             result = new ReusableParameterizedMessage();
@@ -58,7 +66,7 @@ public final class ReusableMessageFactory implements MessageFactory, Serializabl
         return result;
     }
 
-    private static ReusableSimpleMessage getSimple() {
+    private ReusableSimpleMessage getSimple() {
         ReusableSimpleMessage result = threadLocalSimpleMessage.get();
         if (result == null) {
             result = new ReusableSimpleMessage();
@@ -67,7 +75,7 @@ public final class ReusableMessageFactory implements MessageFactory, Serializabl
         return result;
     }
 
-    private static ReusableObjectMessage getObject() {
+    private ReusableObjectMessage getObject() {
         ReusableObjectMessage result = threadLocalObjectMessage.get();
         if (result == null) {
             result = new ReusableObjectMessage();
@@ -87,7 +95,8 @@ public final class ReusableMessageFactory implements MessageFactory, Serializabl
      */
     @Override
     public Message newMessage(final String message, final Object... params) {
-        return getParameterized().set(message, params);
+        return threadLocalParameterized != null ? getParameterized().set(message, params) :
+                new ParameterizedMessage(message, params);
     }
 
     /**
@@ -100,9 +109,12 @@ public final class ReusableMessageFactory implements MessageFactory, Serializabl
      */
     @Override
     public Message newMessage(final String message) {
-        ReusableSimpleMessage result = getSimple();
-        result.set(message);
-        return result;
+        if (threadLocalSimpleMessage != null) {
+            ReusableSimpleMessage result = getSimple();
+            result.set(message);
+            return result;
+        }
+        return new SimpleMessage(message);
     }
 
 
@@ -116,8 +128,11 @@ public final class ReusableMessageFactory implements MessageFactory, Serializabl
      */
     @Override
     public Message newMessage(final Object message) {
-        ReusableObjectMessage result = getObject();
-        result.set(message);
-        return result;
+        if (threadLocalObjectMessage != null) {
+            ReusableObjectMessage result = getObject();
+            result.set(message);
+            return result;
+        }
+        return new ObjectMessage(message);
     }
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/53a4b152/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java
index c982294..ae71065 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java
@@ -18,6 +18,9 @@ package org.apache.logging.log4j.spi;
 
 import java.io.Serializable;
 
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Marker;
 import org.apache.logging.log4j.MarkerManager;
@@ -36,6 +39,8 @@ import org.apache.logging.log4j.util.MessageSupplier;
 import org.apache.logging.log4j.util.PropertiesUtil;
 import org.apache.logging.log4j.util.Strings;
 import org.apache.logging.log4j.util.Supplier;
+import org.apache.logging.log4j.util.ThreadLocalRegistry;
+import org.apache.logging.log4j.util.ThreadLocalRegistryAware;
 
 /**
  * Base implementation of a Logger. It is highly recommended that any Logger implementation extend this class.
@@ -94,12 +99,15 @@ public abstract class AbstractLogger implements ExtendedLogger, Serializable {
     private final String name;
     private final MessageFactory messageFactory;
     private final FlowMessageFactory flowMessageFactory;
+    private final transient ThreadLocalRegistry registry;
 
     /**
      * Creates a new logger named after this class (or subclass).
      */
     public AbstractLogger() {
+        super();
         this.name = getClass().getName();
+        this.registry = null;
         this.messageFactory = createDefaultMessageFactory();
         this.flowMessageFactory = createDefaultFlowMessageFactory();
     }
@@ -110,7 +118,11 @@ public abstract class AbstractLogger implements ExtendedLogger, Serializable {
      * @param name the logger name
      */
     public AbstractLogger(final String name) {
-        this(name, createDefaultMessageFactory());
+        super();
+        this.name = name;
+        this.registry = null;
+        this.messageFactory = createDefaultMessageFactory();
+        this.flowMessageFactory = createDefaultFlowMessageFactory();
     }
 
     /**
@@ -120,7 +132,17 @@ public abstract class AbstractLogger implements ExtendedLogger, Serializable {
      * @param messageFactory the message factory, if null then use the default message factory.
      */
     public AbstractLogger(final String name, final MessageFactory messageFactory) {
+        super();
         this.name = name;
+        this.registry = null;
+        this.messageFactory = messageFactory == null ? createDefaultMessageFactory() : messageFactory;
+        this.flowMessageFactory = createDefaultFlowMessageFactory();
+    }
+
+    public AbstractLogger(final String name, final MessageFactory messageFactory, ThreadLocalRegistry registry) {
+        super();
+        this.name = name;
+        this.registry = registry;
         this.messageFactory = messageFactory == null ? createDefaultMessageFactory() : messageFactory;
         this.flowMessageFactory = createDefaultFlowMessageFactory();
     }
@@ -221,17 +243,41 @@ public abstract class AbstractLogger implements ExtendedLogger, Serializable {
         }
     }
 
-    private static MessageFactory createDefaultMessageFactory() {
+    private MessageFactory createDefaultMessageFactory() {
         try {
-            return DEFAULT_MESSAGE_FACTORY_CLASS.newInstance();
+            MessageFactory factory = DEFAULT_MESSAGE_FACTORY_CLASS.newInstance();
+            if (factory instanceof ThreadLocalRegistryAware) {
+                try {
+                    Constructor<? extends MessageFactory> constructor =
+                            DEFAULT_MESSAGE_FACTORY_CLASS.getConstructor(ThreadLocalRegistry.class);
+                    factory = constructor.newInstance(registry);
+                } catch (NoSuchMethodException | InvocationTargetException ex) {
+                    factory = DEFAULT_MESSAGE_FACTORY_CLASS.newInstance();
+                }
+            } else {
+                factory = DEFAULT_MESSAGE_FACTORY_CLASS.newInstance();
+            }
+            return factory;
         } catch (final InstantiationException | IllegalAccessException e) {
             throw new IllegalStateException(e);
         }
     }
 
-    private static FlowMessageFactory createDefaultFlowMessageFactory() {
+    private FlowMessageFactory createDefaultFlowMessageFactory() {
         try {
-            return DEFAULT_FLOW_MESSAGE_FACTORY_CLASS.newInstance();
+            FlowMessageFactory factory = DEFAULT_FLOW_MESSAGE_FACTORY_CLASS.newInstance();
+            if (factory instanceof ThreadLocalRegistryAware) {
+                try {
+                    Constructor<? extends FlowMessageFactory> constructor =
+                            DEFAULT_FLOW_MESSAGE_FACTORY_CLASS.getConstructor(ThreadLocalRegistry.class);
+                    factory = constructor.newInstance(registry);
+                } catch (NoSuchMethodException | InvocationTargetException ex) {
+                    factory = DEFAULT_FLOW_MESSAGE_FACTORY_CLASS.newInstance();
+                }
+            } else {
+                factory = DEFAULT_FLOW_MESSAGE_FACTORY_CLASS.newInstance();
+            }
+            return factory;
         } catch (final InstantiationException | IllegalAccessException e) {
             throw new IllegalStateException(e);
         }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/53a4b152/log4j-api/src/main/java/org/apache/logging/log4j/util/ThreadLocalRegistry.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/ThreadLocalRegistry.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/ThreadLocalRegistry.java
new file mode 100644
index 0000000..91756ca
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/ThreadLocalRegistry.java
@@ -0,0 +1,29 @@
+/*
+ * 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.util;
+
+/**
+ *
+ */
+public interface ThreadLocalRegistry {
+
+    ThreadLocal<?> get(String name);
+
+    void remove(String name);
+
+    void clearAndRemoveAll();
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/53a4b152/log4j-api/src/main/java/org/apache/logging/log4j/util/ThreadLocalRegistryAware.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/ThreadLocalRegistryAware.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/ThreadLocalRegistryAware.java
new file mode 100644
index 0000000..dc10974
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/ThreadLocalRegistryAware.java
@@ -0,0 +1,24 @@
+/*
+ * 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.util;
+
+/**
+ *
+ */
+public interface ThreadLocalRegistryAware {
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/53a4b152/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java
index 83996c4..977bc30 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java
@@ -62,13 +62,13 @@ public class Logger extends AbstractLogger implements Supplier<LoggerConfig> {
 
     /**
      * The constructor.
-     * 
+     *
      * @param context The LoggerContext this Logger is associated with.
      * @param messageFactory The message factory.
      * @param name The name of the Logger.
      */
     protected Logger(final LoggerContext context, final String name, final MessageFactory messageFactory) {
-        super(name, messageFactory);
+        super(name, messageFactory, context.getThreadLocalRegistry());
         this.context = context;
         privateConfig = new PrivateConfig(context.getConfiguration(), this);
     }
@@ -99,7 +99,7 @@ public class Logger extends AbstractLogger implements Supplier<LoggerConfig> {
 
     /**
      * Returns the LoggerContext this Logger is associated with.
-     * 
+     *
      * @return the LoggerContext.
      */
     public LoggerContext getContext() {
@@ -111,7 +111,7 @@ public class Logger extends AbstractLogger implements Supplier<LoggerConfig> {
      * <p>
      * If the new level is null, this logger inherits the level from its parent.
      * </p>
-     * 
+     *
      * @param level The Level to use on this Logger, may be null.
      */
     public synchronized void setLevel(final Level level) {
@@ -130,7 +130,7 @@ public class Logger extends AbstractLogger implements Supplier<LoggerConfig> {
 
     /*
      * (non-Javadoc)
-     * 
+     *
      * @see org.apache.logging.log4j.util.Supplier#get()
      */
     @Override
@@ -173,7 +173,7 @@ public class Logger extends AbstractLogger implements Supplier<LoggerConfig> {
 
     /**
      * This method is not exposed through the public API and is used primarily for unit testing.
-     * 
+     *
      * @param appender The Appender to add to the Logger.
      */
     public void addAppender(final Appender appender) {
@@ -182,7 +182,7 @@ public class Logger extends AbstractLogger implements Supplier<LoggerConfig> {
 
     /**
      * This method is not exposed through the public API and is used primarily for unit testing.
-     * 
+     *
      * @param appender The Appender to remove from the Logger.
      */
     public void removeAppender(final Appender appender) {
@@ -191,7 +191,7 @@ public class Logger extends AbstractLogger implements Supplier<LoggerConfig> {
 
     /**
      * This method is not exposed through the public API and is used primarily for unit testing.
-     * 
+     *
      * @return A Map containing the Appender's name as the key and the Appender as the value.
      */
     public Map<String, Appender> getAppenders() {
@@ -200,7 +200,7 @@ public class Logger extends AbstractLogger implements Supplier<LoggerConfig> {
 
     /**
      * This method is not exposed through the public API and is used primarily for unit testing.
-     * 
+     *
      * @return An Iterator over all the Filters associated with the Logger.
      */
     // FIXME: this really ought to be an Iterable instead of an Iterator
@@ -229,7 +229,7 @@ public class Logger extends AbstractLogger implements Supplier<LoggerConfig> {
 
     /**
      * This method is not exposed through the public API and is used primarily for unit testing.
-     * 
+     *
      * @return The number of Filters associated with the Logger.
      */
     public int filterCount() {
@@ -244,7 +244,7 @@ public class Logger extends AbstractLogger implements Supplier<LoggerConfig> {
 
     /**
      * This method is not exposed through the public API and is used primarily for unit testing.
-     * 
+     *
      * @param filter The Filter to add.
      */
     public void addFilter(final Filter filter) {
@@ -254,7 +254,7 @@ public class Logger extends AbstractLogger implements Supplier<LoggerConfig> {
     /**
      * This method is not exposed through the public API and is present only to support the Log4j 1.2 compatibility
      * bridge.
-     * 
+     *
      * @return true if the associated LoggerConfig is additive, false otherwise.
      */
     public boolean isAdditive() {
@@ -264,7 +264,7 @@ public class Logger extends AbstractLogger implements Supplier<LoggerConfig> {
     /**
      * This method is not exposed through the public API and is present only to support the Log4j 1.2 compatibility
      * bridge.
-     * 
+     *
      * @param additive Boolean value to indicate whether the Logger is additive or not.
      */
     public void setAdditive(final boolean additive) {
@@ -431,7 +431,7 @@ public class Logger extends AbstractLogger implements Supplier<LoggerConfig> {
 
     /**
      * Returns a String representation of this instance in the form {@code "name:level[ in context_name]"}.
-     * 
+     *
      * @return A String describing this Logger instance.
      */
     @Override

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/53a4b152/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
----------------------------------------------------------------------
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 3937805..efb838c 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
@@ -43,9 +43,11 @@ import org.apache.logging.log4j.core.util.NetUtils;
 import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
 import org.apache.logging.log4j.message.MessageFactory;
 import org.apache.logging.log4j.spi.AbstractLogger;
+import org.apache.logging.log4j.core.util.DefaultThreadLocalRegistry;
 import org.apache.logging.log4j.spi.LoggerContextFactory;
 import org.apache.logging.log4j.spi.LoggerContextKey;
 import org.apache.logging.log4j.spi.Terminable;
+import org.apache.logging.log4j.util.ThreadLocalRegistry;
 
 import static org.apache.logging.log4j.core.util.ShutdownCallbackRegistry.*;
 
@@ -66,6 +68,7 @@ public class LoggerContext extends AbstractLifeCycle implements org.apache.loggi
 
     private final ConcurrentMap<String, Logger> loggers = new ConcurrentHashMap<>();
     private final CopyOnWriteArrayList<PropertyChangeListener> propertyChangeListeners = new CopyOnWriteArrayList<>();
+    private final ThreadLocalRegistry threadLocalRegistry = new DefaultThreadLocalRegistry();
 
     /**
      * The Configuration is volatile to guarantee that initialization of the Configuration has completed before the
@@ -323,6 +326,10 @@ public class LoggerContext extends AbstractLifeCycle implements org.apache.loggi
         return contextName;
     }
 
+    public ThreadLocalRegistry getThreadLocalRegistry() {
+        return threadLocalRegistry;
+    }
+
     /**
      * Gets the root logger.
      *

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/53a4b152/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DefaultThreadLocalRegistry.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DefaultThreadLocalRegistry.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DefaultThreadLocalRegistry.java
new file mode 100644
index 0000000..e7664f2
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DefaultThreadLocalRegistry.java
@@ -0,0 +1,52 @@
+/*
+ * 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.util;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.logging.log4j.util.ThreadLocalRegistry;
+
+/**
+ * ThreadLocalRegistry.
+ */
+public class DefaultThreadLocalRegistry implements ThreadLocalRegistry {
+
+    private ConcurrentMap<String, Log4jThreadLocal<?>> threadLocalMap = new ConcurrentHashMap<>();
+
+    @Override
+    public ThreadLocal<?> get(String name) {
+        Log4jThreadLocal<?> threadLocal = threadLocalMap.get(name);
+        if (threadLocal != null) {
+            return threadLocal;
+        }
+        return threadLocalMap.putIfAbsent(name, new Log4jThreadLocal());
+    }
+
+    @Override
+    public void remove(String name) {
+        threadLocalMap.remove(name);
+    }
+
+    @Override
+    public void clearAndRemoveAll() {
+        for (Log4jThreadLocal<?> threadLocal : threadLocalMap.values()) {
+            threadLocal.clear();
+        }
+        threadLocalMap.clear();
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/53a4b152/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Log4jThreadLocal.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Log4jThreadLocal.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Log4jThreadLocal.java
new file mode 100644
index 0000000..aacdb36
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Log4jThreadLocal.java
@@ -0,0 +1,81 @@
+/*
+ * 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.util;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * ThreadLocal that can clean up all threads.
+ * @param <T> The Object type being stored in the ThreadLocal.
+ */
+public class Log4jThreadLocal<T> extends ThreadLocal<T> {
+
+    private ConcurrentMap<Long, AtomicReference<T>> containers = new ConcurrentHashMap<>();
+    private ThreadLocal<AtomicReference<T>> threadLocal = new ThreadLocal<>();
+
+    @Override
+    public void set(T value) {
+        AtomicReference<T> container = getContainer(Thread.currentThread().getId());
+        container.set(value);
+        threadLocal.set(container);
+    }
+
+    @Override
+    public T get() {
+        AtomicReference<T> container = threadLocal.get();
+        if (container != null) {
+            return container.get();
+        }
+        long id = Thread.currentThread().getId();
+        container = getContainer(id);
+        T value = initialValue();
+        container.set(value);
+        containers.put(id, container);
+        return value;
+    }
+
+    @Override
+    public void remove() {
+        AtomicReference<T> container = threadLocal.get();
+        containers.remove(Thread.currentThread().getId());
+        threadLocal.remove();
+        container.set(null);
+    }
+
+    public void clear() {
+        Iterator<Map.Entry<Long, AtomicReference<T>>> iterator = containers.entrySet().iterator();
+        while (iterator.hasNext()) {
+            Map.Entry<Long, AtomicReference<T>> entry = iterator.next();
+            iterator.remove();
+            entry.setValue(null);
+        }
+    }
+
+    private AtomicReference<T> getContainer(long id) {
+        AtomicReference<T> container = containers.get(id);
+        if (container == null) {
+            container = new AtomicReference<>();
+            containers.put(Thread.currentThread().getId(), container);
+        }
+        return container;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/53a4b152/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/StackTraceBenchmark.java
----------------------------------------------------------------------
diff --git a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/StackTraceBenchmark.java b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/StackTraceBenchmark.java
new file mode 100644
index 0000000..9d85222
--- /dev/null
+++ b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/StackTraceBenchmark.java
@@ -0,0 +1,110 @@
+/*
+ * 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.perf.jmh;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadMXBean;
+/*
+import java.lang.StackWalker;
+import java.lang.StackWalker.StackFrame;
+import java.util.List;
+import java.util.stream.Collectors; */
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.infra.Blackhole;
+
+@State(Scope.Thread)
+public class StackTraceBenchmark {
+    private ThreadMXBean threadBean;
+
+    @Setup
+    public void setup() {
+        threadBean = ManagementFactory.getThreadMXBean();
+    }
+
+    @Benchmark
+    public void testBaseline(final Blackhole bh) {
+    }
+
+    @Benchmark
+    public StackTraceElement[] getStackTraceFromException() {
+        return new Throwable().getStackTrace();
+    }
+
+    @Benchmark
+    public StackTraceElement[] getStackTraceFromThread() {
+        return Thread.currentThread().getStackTrace();
+    }
+
+    @Benchmark
+    public StackTraceElement[] getStackTraceFromThrowableInfo() {
+        long id = Thread.currentThread().getId();
+        return threadBean.getThreadInfo(new long[] {id}, false, false)[0].getStackTrace();
+    }
+
+    @Benchmark
+    public StackTraceElement[] getSubsetStackTraceFromThrowableInfo() {
+        long id = Thread.currentThread().getId();
+        return threadBean.getThreadInfo(id, 15).getStackTrace();
+    }
+    /*
+    @Benchmark
+    public List<StackFrame> getStackFrames() {
+        return  StackWalker.getInstance().walk(s ->
+                s.limit(15).collect(Collectors.toList()));
+    } */
+
+    /*
+    @Benchmark
+    public String getMethodFromThrowable() {
+        return calcLocation("getMethodFromThrowable");
+
+    }
+
+    private String calcLocation(String methodName) {
+        final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
+        StackTraceElement last = null;
+        for (int i = stackTrace.length - 1; i > 0; i--) {
+            if (methodName.equals(stackTrace[i].getMethodName())) {
+                return last.getMethodName();
+            }
+            last = stackTrace[i];
+        }
+        return null;
+    }
+
+    @Benchmark
+    public String getMethodFromStackWalker() {
+        StackWalker walker = StackWalker.getInstance();
+        walker.walk()
+    } */
+
+
+    // ============================== HOW TO RUN THIS TEST: ====================================
+    //
+    // In sampling mode (latency test):
+    // java -jar log4j-perf/target/benchmarks.jar ".*StackTraceBenchmark.*" -i 5 -f 1 -wi 5 -bm sample -tu ns
+    //
+    // Throughput test:
+    // java -jar benchmarks.jar ".*StackTraceBenchmark.*" -i 5 -f 1 -wi 5 -bm Throughput -tu ms
+    //
+    // Usage help:
+    // java -jar log4j-perf/target/benchmarks.jar -help
+    //
+}