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
This commit is contained in:
Collin Smith 2020-08-27 01:17:25 -07:00
parent 60d50fa15d
commit d9232b3593
4 changed files with 269 additions and 26 deletions

View File

@ -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<String, String> mdc;
private final StringMap mdc;
LogEvent(
Level level,
Message message,
StackTraceElement source,
UnmodifiableOrderedMap<String, String> mdc) {
StringMap mdc) {
this.level = level;
this.message = message;
this.source = source;
@ -33,7 +31,7 @@ public class LogEvent {
return source;
}
public UnmodifiableOrderedMap<String, String> mdc() {
public StringMap mdc() {
return mdc;
}
}

View File

@ -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<OrderedMap<String, String>> threadLocal
= new ThreadLocal<OrderedMap<String, String>>() {
private static final ThreadLocal<StringMap> threadLocal
= new ThreadLocal<StringMap>() {
@Override
protected OrderedMap<String, String> 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<String, String> freeze() {
return (UnmodifiableOrderedMap<String,String>)
UnmodifiableOrderedMap.unmodifiableOrderedMap(threadLocal.get());
public static StringMap freeze() {
return threadLocal.get().freeze();
}
}

View File

@ -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<String, String> 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<String, String> mdc = event.mdc();
final StringMap mdc = event.mdc();
encodeMessage(event, buffer);
encodeFullMDC(mdc, buffer);
}
private void encodeFullMDC(OrderedMap<String, String> 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<String, String> 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<String, String> 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);
}
}
}

View File

@ -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<String> 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<Entry> {
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;
}
}
}