From d9232b35931e866cba3120dcbdd6a29256fe7083 Mon Sep 17 00:00:00 2001 From: Collin Smith Date: Thu, 27 Aug 2020 01:17:25 -0700 Subject: [PATCH] Created StringMap class to manage thread context Created StringMap class to manage thread context StringMap is ordered and optimized for my use-case Engineered compact encoding mode to track thread context --- core/src/com/riiablo/logger/LogEvent.java | 8 +- core/src/com/riiablo/logger/MDC.java | 17 +- .../com/riiablo/logger/RiiabloEncoder.java | 31 ++- core/src/com/riiablo/logger/StringMap.java | 239 ++++++++++++++++++ 4 files changed, 269 insertions(+), 26 deletions(-) create mode 100644 core/src/com/riiablo/logger/StringMap.java diff --git a/core/src/com/riiablo/logger/LogEvent.java b/core/src/com/riiablo/logger/LogEvent.java index 48663a2a..faecc79e 100644 --- a/core/src/com/riiablo/logger/LogEvent.java +++ b/core/src/com/riiablo/logger/LogEvent.java @@ -1,20 +1,18 @@ package com.riiablo.logger; -import org.apache.commons.collections4.map.UnmodifiableOrderedMap; - import com.riiablo.logger.message.Message; public class LogEvent { private final Level level; private final Message message; private final StackTraceElement source; - private final UnmodifiableOrderedMap mdc; + private final StringMap mdc; LogEvent( Level level, Message message, StackTraceElement source, - UnmodifiableOrderedMap mdc) { + StringMap mdc) { this.level = level; this.message = message; this.source = source; @@ -33,7 +31,7 @@ public class LogEvent { return source; } - public UnmodifiableOrderedMap mdc() { + public StringMap mdc() { return mdc; } } diff --git a/core/src/com/riiablo/logger/MDC.java b/core/src/com/riiablo/logger/MDC.java index ff0ec200..8906aafe 100644 --- a/core/src/com/riiablo/logger/MDC.java +++ b/core/src/com/riiablo/logger/MDC.java @@ -1,17 +1,13 @@ package com.riiablo.logger; -import org.apache.commons.collections4.OrderedMap; -import org.apache.commons.collections4.map.ListOrderedMap; -import org.apache.commons.collections4.map.UnmodifiableOrderedMap; - public class MDC { private MDC() {} - private static final ThreadLocal> threadLocal - = new ThreadLocal>() { + private static final ThreadLocal threadLocal + = new ThreadLocal() { @Override - protected OrderedMap initialValue() { - return new ListOrderedMap<>(); // TODO: copy on write map + protected StringMap initialValue() { + return new StringMap(); } }; @@ -39,8 +35,7 @@ public class MDC { return threadLocal.get().size(); } - public static UnmodifiableOrderedMap freeze() { - return (UnmodifiableOrderedMap) - UnmodifiableOrderedMap.unmodifiableOrderedMap(threadLocal.get()); + public static StringMap freeze() { + return threadLocal.get().freeze(); } } diff --git a/core/src/com/riiablo/logger/RiiabloEncoder.java b/core/src/com/riiablo/logger/RiiabloEncoder.java index f2024eb0..5569f279 100644 --- a/core/src/com/riiablo/logger/RiiabloEncoder.java +++ b/core/src/com/riiablo/logger/RiiabloEncoder.java @@ -1,7 +1,6 @@ package com.riiablo.logger; import java.io.OutputStream; -import org.apache.commons.collections4.OrderedMap; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -12,8 +11,9 @@ public class RiiabloEncoder extends SimpleEncoder { private final StringBuilder buffer = new StringBuilder(1024); + private Thread context; private boolean fullMode; - private OrderedMap mdc; + private StringMap mdc; private int depth; public boolean isFullMode() { @@ -43,22 +43,28 @@ public class RiiabloEncoder extends SimpleEncoder { } private void encodeFullMode(LogEvent event, StringBuilder buffer) { - final OrderedMap mdc = event.mdc(); + final StringMap mdc = event.mdc(); encodeMessage(event, buffer); encodeFullMDC(mdc, buffer); } - private void encodeFullMDC(OrderedMap mdc, StringBuilder buffer) { + private void encodeFullMDC(StringMap mdc, StringBuilder buffer) { if (mdc.isEmpty()) return; buffer.append(' '); buffer.append(mdc.toString()); } private void encodeCompactMode(LogEvent event, StringBuilder buffer) { - final OrderedMap mdc = event.mdc(); + final StringMap mdc = event.mdc(); final int depth = mdc.size(); if (depth > 0) { - if (!mdc.equals(this.mdc) || this.depth != depth) { + final Thread currentThread = Thread.currentThread(); + if (context != currentThread) { + context = currentThread; + this.depth = 0; + } + + if (!mdc.equals(this.mdc)) { encodeCompactMDC(mdc, buffer, depth); this.mdc = mdc; this.depth = depth; @@ -70,9 +76,14 @@ public class RiiabloEncoder extends SimpleEncoder { encodeMessage(event, buffer); } - private void encodeCompactMDC(OrderedMap mdc, StringBuilder buffer, int depth) { - buffer.append(spaces, 0, (depth - 1) * DEPTH_STEP); - encodeFullMDC(mdc, buffer); - buffer.append(lineSeparator); + private void encodeCompactMDC(StringMap mdc, StringBuilder buffer, int depth) { + for (int d = this.depth; d < depth; d++) { + buffer.append(spaces, 0, d * DEPTH_STEP); + buffer.append(' '); + buffer.append('{'); + mdc.appendEntry(d, buffer); + buffer.append('}'); + buffer.append(lineSeparator); + } } } diff --git a/core/src/com/riiablo/logger/StringMap.java b/core/src/com/riiablo/logger/StringMap.java new file mode 100644 index 00000000..c1edea7a --- /dev/null +++ b/core/src/com/riiablo/logger/StringMap.java @@ -0,0 +1,239 @@ +package com.riiablo.logger; + +import java.util.Arrays; +import java.util.Iterator; +import org.apache.commons.lang3.ArrayUtils; + +import com.badlogic.gdx.utils.ObjectIntMap; + +public class StringMap { + private static final int DEFAULT_CAPACITY = 4; + private final ObjectIntMap indexes; + private String[] keys; + private String[] vals; + private int size; + + private boolean immutable; + private StringMap immutableCopy; + private String toString; + + public StringMap() { + keys = ArrayUtils.EMPTY_STRING_ARRAY; + vals = ArrayUtils.EMPTY_STRING_ARRAY; + indexes = new ObjectIntMap<>(DEFAULT_CAPACITY); + } + + private StringMap(StringMap other) { + assert other.keys.length == other.vals.length; + assert other.size == other.indexes.size; + size = other.size; + keys = Arrays.copyOf(other.keys, size); + vals = Arrays.copyOf(other.vals, size); + indexes = new ObjectIntMap<>(other.indexes); + immutable = true; + } + + private void inflateTable(final int size) { + assert !immutable; + assert this.size == 0; + keys = new String[size]; + vals = new String[size]; + } + + private void ensureCapacity(final int size) { + assert !immutable; + assert keys.length == vals.length; + if (size >= this.size) { + final int resize = Math.max(this.size * 2, DEFAULT_CAPACITY); + assert resize > size; + keys = Arrays.copyOf(keys, resize); + vals = Arrays.copyOf(vals, resize); + } + } + + private void assertMutable() { + if (immutable) { + throw new UnsupportedOperationException("StringMap has been frozen."); + } + } + + public void put(String key, String value) { + assertMutable(); + if (immutableCopy != null) { + immutableCopy = null; + } + + assert keys.length == vals.length; + if (keys.length == 0) { + inflateTable(DEFAULT_CAPACITY); + } + + assert keys != null && vals != null; + final int index = indexes.get(key, -1); + if (index >= 0) { + vals[index] = value; + } else { + ensureCapacity(size + 1); + keys[size] = key; + vals[size] = value; + indexes.put(key, size); + size++; + } + assert size == indexes.size; + assert size > 0; + } + + public String get(String key) { + assert keys.length == vals.length; + if (keys.length == 0) { + return null; + } + + final int index = indexes.get(key, -1); + return index >= 0 ? vals[index] : null; + } + + public void remove(String key) { + assertMutable(); + if (immutableCopy != null) { + immutableCopy = null; + } + + assert keys.length == vals.length; + if (keys.length == 0) { + return; + } + + final int index = indexes.get(key, -1); + if (index < 0) return; + System.arraycopy(keys, index + 1, keys, index, size - index - 1); + System.arraycopy(vals, index + 1, vals, index, size - index - 1); + keys[size] = vals[size] = null; + indexes.remove(key, -1); + size--; + assert size == indexes.size; + } + + public void clear() { + assert keys != null || keys == vals; + if (keys == null) { + assert size == 0; + return; + } + + Arrays.fill(keys, null); + Arrays.fill(vals, null); + indexes.clear(); + size = 0; + assert size == indexes.size; + } + + public int size() { + assert size == indexes.size; + return size; + } + + public boolean isEmpty() { + assert size == indexes.size; + return size == 0; + } + + public StringMap freeze() { + if (immutableCopy == null) { + return immutableCopy = new StringMap(this); + } else { + return immutableCopy; + } + } + + @Override + public String toString() { + if (toString == null) { + StringBuilder buffer = new StringBuilder(256); + buffer.append('{'); + for (int i = 0, s = size; i < s; i++) { + appendEntry(i, buffer).append(','); + } + + if (size > 0) { + buffer.setLength(buffer.length() - 1); + } + + buffer.append('}'); + toString = buffer.toString(); + } + + return toString; + } + + StringBuilder appendEntry(int index, StringBuilder buffer) { + assert index >= 0 && index < size; + return buffer.append(keys[index]).append('=').append(vals[index]); + } + + private void assertImmutable() { + if (!immutable) { + throw new UnsupportedOperationException("StringMap has not been frozen."); + } + } + + public StringMapIterator iterator() { + return iterator(0); + } + + public StringMapIterator iterator(int startIndex) { + if (startIndex < 0) { + throw new IllegalArgumentException("startIndex(" + startIndex + ") < " + 0); + } + assertImmutable(); + return new StringMapIterator(startIndex); + } + + public class StringMapIterator implements Iterator { + final Entry entry = new Entry(); + int index; + + StringMapIterator(int index) { + assert immutable; + this.index = index; + } + + @Override + public boolean hasNext() { + return index < size; + } + + @Override + public Entry next() { + entry.key = keys[index]; + entry.value = vals[index]; + index++; + return entry; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + public static class Entry { + String key; + String value; + + Entry() {} + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return key + "=" + value; + } + } +}