add support for keyed deques in MDC

Signed-off-by: Ceki Gulcu <ceki@qos.ch>
This commit is contained in:
Ceki Gulcu 2022-01-05 22:10:34 +01:00
parent 12b5a6b676
commit f09e33dd15
6 changed files with 131 additions and 35 deletions

View File

@ -25,6 +25,7 @@
package org.slf4j; package org.slf4j;
import java.io.Closeable; import java.io.Closeable;
import java.util.Deque;
import java.util.Map; import java.util.Map;
import org.slf4j.helpers.BasicMDCAdapter; import org.slf4j.helpers.BasicMDCAdapter;
@ -64,6 +65,7 @@ import org.slf4j.spi.SLF4JServiceProvider;
public class MDC { public class MDC {
static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA"; static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA";
private static final String MDC_APAPTER_CANNOT_BE_NULL_MESSAGE = "MDCAdapter cannot be null. See also " + NULL_MDCA_URL;
static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder"; static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder";
static MDCAdapter mdcAdapter; static MDCAdapter mdcAdapter;
@ -116,7 +118,7 @@ public class MDC {
throw new IllegalArgumentException("key parameter cannot be null"); throw new IllegalArgumentException("key parameter cannot be null");
} }
if (mdcAdapter == null) { if (mdcAdapter == null) {
throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
} }
mdcAdapter.put(key, val); mdcAdapter.put(key, val);
} }
@ -172,7 +174,7 @@ public class MDC {
} }
if (mdcAdapter == null) { if (mdcAdapter == null) {
throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
} }
return mdcAdapter.get(key); return mdcAdapter.get(key);
} }
@ -193,7 +195,7 @@ public class MDC {
} }
if (mdcAdapter == null) { if (mdcAdapter == null) {
throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
} }
mdcAdapter.remove(key); mdcAdapter.remove(key);
} }
@ -203,7 +205,7 @@ public class MDC {
*/ */
public static void clear() { public static void clear() {
if (mdcAdapter == null) { if (mdcAdapter == null) {
throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
} }
mdcAdapter.clear(); mdcAdapter.clear();
} }
@ -217,7 +219,7 @@ public class MDC {
*/ */
public static Map<String, String> getCopyOfContextMap() { public static Map<String, String> getCopyOfContextMap() {
if (mdcAdapter == null) { if (mdcAdapter == null) {
throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
} }
return mdcAdapter.getCopyOfContextMap(); return mdcAdapter.getCopyOfContextMap();
} }
@ -235,7 +237,7 @@ public class MDC {
*/ */
public static void setContextMap(Map<String, String> contextMap) { public static void setContextMap(Map<String, String> contextMap) {
if (mdcAdapter == null) { if (mdcAdapter == null) {
throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
} }
mdcAdapter.setContextMap(contextMap); mdcAdapter.setContextMap(contextMap);
} }
@ -250,4 +252,48 @@ public class MDC {
return mdcAdapter; return mdcAdapter;
} }
/**
* Push a value into the deque(stack) referenced by 'key'.
*
* @param key identifies the appropriate stack
* @param value the value to push into the stack
* @since 2.0.0
*/
static public void pushByKey(String key, String value) {
if (mdcAdapter == null) {
throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
}
mdcAdapter.pushByKey(key, value);
}
/**
* Pop the stack referenced by 'key' and return the value possibly null.
*
* @param key identifies the deque(stack)
* @return the value just popped. May be null/
* @since 2.0.0
*/
static public String popByKey(String key) {
if (mdcAdapter == null) {
throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
}
return mdcAdapter.popByKey(key);
}
/**
* Returns a copy of the deque(stack) referenced by 'key'. May be null.
*
* @param key identifies the stack
* @return copy of stack referenced by 'key'. May be null.
*
* @since 2.0.0
*/
public Deque<String> getCopyOfDequeByKey(String key) {
if (mdcAdapter == null) {
throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
}
return mdcAdapter.getCopyOfDequeByKey(key);
}
} }

View File

@ -43,7 +43,7 @@ import java.util.*;
*/ */
public class BasicMDCAdapter implements MDCAdapter { public class BasicMDCAdapter implements MDCAdapter {
private final ThreadLocalMapOfStacks threadLocalMapOfStacks = new ThreadLocalMapOfStacks(); private final ThreadLocalMapOfStacks threadLocalMapOfDeques = new ThreadLocalMapOfStacks();
private final InheritableThreadLocal<Map<String, String>> inheritableThreadLocalMap = new InheritableThreadLocal<Map<String, String>>() { private final InheritableThreadLocal<Map<String, String>> inheritableThreadLocalMap = new InheritableThreadLocal<Map<String, String>>() {
@Override @Override
@ -151,16 +151,20 @@ public class BasicMDCAdapter implements MDCAdapter {
@Override @Override
public void pushByKey(String key, String value) { public void pushByKey(String key, String value) {
threadLocalMapOfStacks.pushByKey(key, value); threadLocalMapOfDeques.pushByKey(key, value);
} }
@Override @Override
public String popByKey(String key) { public String popByKey(String key) {
return threadLocalMapOfStacks.popByKey(key); return threadLocalMapOfDeques.popByKey(key);
} }
@Override @Override
public Deque<String> getCopyOfStackByKey(String key) { public Deque<String> getCopyOfDequeByKey(String key) {
return threadLocalMapOfStacks.getCopyOfStackByKey(key); return threadLocalMapOfDeques.getCopyOfDequeByKey(key);
}
@Override
public void clearDequeByKey(String key) {
threadLocalMapOfDeques.clearDequeByKey(key);
} }
} }

View File

@ -71,8 +71,11 @@ public class NOPMDCAdapter implements MDCAdapter {
} }
@Override @Override
public Deque<String> getCopyOfStackByKey(String key) { public Deque<String> getCopyOfDequeByKey(String key) {
return null; return null;
} }
public void clearDequeByKey(String key) {
}
} }

View File

@ -5,6 +5,14 @@ import java.util.Deque;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
/**
* A simple implementation of ThreadLocal backed Map containing values of type
* Deque<String>.
*
* @author Ceki Guuml;c&uuml;
* @since 2.0.0
*/
public class ThreadLocalMapOfStacks { public class ThreadLocalMapOfStacks {
final ThreadLocal<Map<String, Deque<String>>> tlMapOfStacks = new ThreadLocal<>(); final ThreadLocal<Map<String, Deque<String>>> tlMapOfStacks = new ThreadLocal<>();
@ -20,12 +28,12 @@ public class ThreadLocalMapOfStacks {
tlMapOfStacks.set(map); tlMapOfStacks.set(map);
} }
Deque<String> stack = map.get(key); Deque<String> deque = map.get(key);
if (stack == null) { if (deque == null) {
stack = new ArrayDeque<>(); deque = new ArrayDeque<>();
} }
stack.push(value); deque.push(value);
map.put(key, stack); map.put(key, deque);
} }
public String popByKey(String key) { public String popByKey(String key) {
@ -35,24 +43,44 @@ public class ThreadLocalMapOfStacks {
Map<String, Deque<String>> map = tlMapOfStacks.get(); Map<String, Deque<String>> map = tlMapOfStacks.get();
if (map == null) if (map == null)
return null; return null;
Deque<String> stack = map.get(key); Deque<String> deque = map.get(key);
if (stack == null) if (deque == null)
return null; return null;
return stack.pop(); return deque.pop();
} }
public Deque<String> getCopyOfStackByKey(String key) { public Deque<String> getCopyOfDequeByKey(String key) {
if (key == null) if (key == null)
return null; return null;
Map<String, Deque<String>> map = tlMapOfStacks.get(); Map<String, Deque<String>> map = tlMapOfStacks.get();
if (map == null) if (map == null)
return null; return null;
Deque<String> stack = map.get(key); Deque<String> deque = map.get(key);
if (stack == null) if (deque == null)
return null; return null;
return new ArrayDeque<String>(stack); return new ArrayDeque<String>(deque);
}
/**
* Clear the deque(stack) referenced by 'key'.
*
* @param key identifies the stack
*
* @since 2.0.0
*/
public void clearDequeByKey(String key) {
if (key == null)
return;
Map<String, Deque<String>> map = tlMapOfStacks.get();
if (map == null)
return;
Deque<String> deque = map.get(key);
if (deque == null)
return;
deque.clear();
} }
} }

View File

@ -93,8 +93,8 @@ public interface MDCAdapter {
public void setContextMap(Map<String, String> contextMap); public void setContextMap(Map<String, String> contextMap);
/** /**
* Push a value into the stack referenced by 'key'. * Push a value into the deque(stack) referenced by 'key'.
* * *
* @param key identifies the appropriate stack * @param key identifies the appropriate stack
* @param value the value to push into the stack * @param value the value to push into the stack
* @since 2.0.0 * @since 2.0.0
@ -104,19 +104,30 @@ public interface MDCAdapter {
/** /**
* Pop the stack referenced by 'key' and return the value possibly null. * Pop the stack referenced by 'key' and return the value possibly null.
* *
* @param key identifies the stack * @param key identifies the deque(stack)
* @return the value just popped. May be null/ * @return the value just popped. May be null/
* @since 2.0.0 * @since 2.0.0
*/ */
public String popByKey(String key); public String popByKey(String key);
/** /**
* Returns a copy of the stack referenced by 'key'. May be null. * Returns a copy of the deque(stack) referenced by 'key'. May be null.
* *
* @param key identifies the stack * @param key identifies the stack
* @return copy of stack referenced by 'key'. May be null. * @return copy of stack referenced by 'key'. May be null.
* *
* @since 2.0.0 * @since 2.0.0
*/ */
public Deque<String> getCopyOfStackByKey(String key); public Deque<String> getCopyOfDequeByKey(String key);
/**
* Clear the deque(stack) referenced by 'key'.
*
* @param key identifies the stack
*
* @since 2.0.0
*/
public void clearDequeByKey(String key);
} }

View File

@ -34,7 +34,7 @@ import org.slf4j.spi.MDCAdapter;
public class Log4jMDCAdapter implements MDCAdapter { public class Log4jMDCAdapter implements MDCAdapter {
private final ThreadLocalMapOfStacks threadLocalMapOfStacks = new ThreadLocalMapOfStacks(); private final ThreadLocalMapOfStacks threadLocalMapOfDeques = new ThreadLocalMapOfStacks();
static { static {
if (VersionUtil.getJavaMajorVersion() >= 9) { if (VersionUtil.getJavaMajorVersion() >= 9) {
@ -113,17 +113,21 @@ public class Log4jMDCAdapter implements MDCAdapter {
@Override @Override
public void pushByKey(String key, String value) { public void pushByKey(String key, String value) {
threadLocalMapOfStacks.pushByKey(key, value); threadLocalMapOfDeques.pushByKey(key, value);
} }
@Override @Override
public String popByKey(String key) { public String popByKey(String key) {
return threadLocalMapOfStacks.popByKey(key); return threadLocalMapOfDeques.popByKey(key);
} }
@Override @Override
public Deque<String> getCopyOfStackByKey(String key) { public Deque<String> getCopyOfDequeByKey(String key) {
return threadLocalMapOfStacks.getCopyOfStackByKey(key); return threadLocalMapOfDeques.getCopyOfDequeByKey(key);
}
@Override
public void clearDequeByKey(String key) {
threadLocalMapOfDeques.clearDequeByKey(key);
} }
} }