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 <[email protected]> Authored: Sun Mar 13 23:56:27 2016 -0700 Committer: Ralph Goers <[email protected]> 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 + // +}
