Description
Currently, every time a Log4jLogEvent object is created, a deep copy is made of both the context map and the context stack. However, expected usage is that only a few objects are pushed onto the stack or put in the context map, while the number of LogEvents is in the thousands or millions.
Essentially, there are far more reads than writes, so using a copy-on-write mechanism should give us better performance.
Example context map put: deep copy (expensive but rare)
public static void put(String key, String value) {
if (!useMap)
Map<String, String> map = localMap.get();
Map<String, String> copy = null;
if (map == null) { copy = new HashMap<String, String>(); } else { copy = new HashMap<String, String>(map); }
copy.put(key, value);
localMap.set(copy);
}
Example context stack push: deep copy (expensive but rare)
public static void push(String message) {
if (!useStack) { return; }
ContextStack stack = localStack.get();
ContextStack copy = null;
if (stack == null)
else
{ copy = stack.copy(); } copy.push(message);
localStack.set(copy);
}
Now, when the Log4jLogEvents are created, they just call ThreadContext.getImmutableContext and getImmutableStack. These methods return an unmodifiable wrapper around the most recent copy.
Example for context map:
public static Map<String, String> getImmutableContext()
Example for context stack:
public static ContextStack getImmutableStack()
Note that ThreadContext.ThreadContextStack needs to be changed to contain an ArrayList rather than extend it, to facilitate making both deep mutable copies and shallow immutable copies.
private static class ThreadContextStack implements ContextStack {
private static final long serialVersionUID = 5050501L;
private List<String> list;
public ThreadContextStack()
{ list = new ArrayList<String>(); }/**
- This constructor uses the specified list as its internal data
- structure unchanged. It does not make a defensive copy.
*/
public ThreadContextStack(List<String> aList) { list = aList; // don't copy! }
/**
- This constructor copies the elements of the specified collection into
- a new list. Changes to the specified collection will not affect this
- {@code ThreadContextStack}
.
{ list = new ArrayList<String>(collection); }
*/
public ThreadContextStack(Collection<String> collection)
public void clear()
{ list.clear(); } public String pop() {
int index = list.size() - 1;
if (index >= 0)
throw new NoSuchElementException("The ThreadContext stack is empty");
}
public String peek() {
int index = list.size() - 1;
if (index >= 0)
return null;
}
public void push(String message)
{ list.add(message); }public int getDepth()
{ return list.size(); }public List<String> asList()
{ return list; } public void trim(int depth) {
if (depth < 0)
while (list.size() > depth)
{ list.remove(list.size() - 1); }}
/**
- Returns a deep copy of this stack.
*/
public ContextStack copy() { return new ThreadContextStack(new ArrayList<>(list)); }}
Attachments
Attachments
Issue Links
- is depended upon by
-
LOG4J2-163 Create asynchronous Logger for low-latency logging
- Closed
- is related to
-
LOG4J2-151 Please facilitate subclassing Logger and LoggerContext (in org.apache.logging.log4j.core)
- Closed