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
+ //
+}