-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
slf4j MDCAdapter with multi-thread-context 支持 #51
Comments
@oldratlee,是否考虑对 |
你说的
从『 提供一个 可以Run的代码说明在 #49 (comment) 思路OK的。 你了解一下如何开发 PS: 最近比较忙没有时间去细看 |
@oldratlee sorry,那是笔误。 就使用而言, //省略
ThreadContext.put(TRACE_ID, TRACE_ID_VALUE);
mtc.get().put(TRACE_ID, TRACE_ID_VALUE); 上述需要你写两步,一步是调用ThreadContext下一步是使用mtc,但都是同样上下文变量的处理。所以这样的集成实际价值不大(也就是要么以MDC的处理方式为主,要么以MTC为主) MDC中也集成了threadlocal的使用,但不清楚是否达到了MTC的效果。MTC对于跨现场的处理中有restore这样的方式(但貌似MDC的实现,即MDCAdapter关联的部分又没有,就不确定是否能达到等同的传递线程变量的效果了) |
另外,看了一下, static {
try {
mdcAdapter = StaticMDCBinder.SINGLETON.getMDCA();
} catch (NoClassDefFoundError ncde) {
mdcAdapter = new NOPMDCAdapter();
String msg = ncde.getMessage();
if (msg != null && msg.indexOf("StaticMDCBinder") != -1) {
Util.report("Failed to load class \"org.slf4j.impl.StaticMDCBinder\".");
Util.report("Defaulting to no-operation MDCAdapter implementation.");
Util
.report("See " + NO_STATIC_MDC_BINDER_URL + " for further details.");
} else {
throw ncde;
}
} catch (Exception e) {
// we should never get here
Util.report("MDC binding unsuccessful.", e);
}
}
public class StaticMDCBinder {
/**
* The unique instance of this class.
*/
public static final StaticMDCBinder SINGLETON = new StaticMDCBinder();
private StaticMDCBinder() {
}
/**
* Currently this method always returns an instance of
* {@link StaticMDCBinder}.
*/
public MDCAdapter getMDCA() {
return new Log4jMDCAdapter();
}
public String getMDCAdapterClassStr() {
return Log4jMDCAdapter.class.getName();
}
} 写死了要加载的 如果 package com.alibaba.mtc;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.slf4j.spi.MDCAdapter;
public class Log4jMDCAdapter implements MDCAdapter {
// =================== MTC ===========================
static MtContextThreadLocal<Map<String, String>> mtc = new MtContextThreadLocal<Map<String, String>>() {
@Override
protected void beforeExecute() {
final Map<String, String> log4j2Context = get();
for (Map.Entry<String, String> entry : log4j2Context.entrySet()) {
org.apache.log4j.MDC.put(entry.getKey(), entry.getValue());
}
}
@Override
protected void afterExecute() {
org.apache.log4j.MDC.clear();
}
@Override
protected Map<String, String> initialValue() {
return new HashMap<String, String>();
}
};
public void clear() {
Map map = org.apache.log4j.MDC.getContext();
if (map != null) {
map.clear();
}
// =================== MTC ===========================
mtc.get().clear();
}
public String get(String key) {
return (String) org.apache.log4j.MDC.get(key);
}
public void put(String key, String val) {
org.apache.log4j.MDC.put(key, val);
// =================== MTC ===========================
mtc.get().put(key, val);
}
public void remove(String key) {
org.apache.log4j.MDC.remove(key);
// =================== MTC ===========================
mtc.get().remove(key);
}
public Map getCopyOfContextMap() {
Map old = org.apache.log4j.MDC.getContext();
if(old != null) {
return new HashMap(old);
} else {
return null;
}
}
public void setContextMap(Map contextMap) {
Map old = org.apache.log4j.MDC.getContext();
if(old == null) {
Iterator entrySetIterator = contextMap.entrySet().iterator();
while(entrySetIterator.hasNext()) {
Map.Entry mapEntry = (Map.Entry) entrySetIterator.next();
org.apache.log4j.MDC.put((String) mapEntry.getKey(), mapEntry.getValue());
// =================== MTC ===========================
mtc.get().put((String) mapEntry.getKey(), (String) mapEntry.getValue());
}
} else {
old.clear();
old.putAll(contextMap);
// =================== MTC ===========================
mtc.get().clear();
mtc.get().putAll(contextMap);
}
}
} |
要加强 如果跨 PS: 可以 |
@oldratlee, 的确。对了,鼎哥能否确定一个事情:就是log4j2的threadContext也有类似的提示:
它上面有一段有暗示但没有提示的话:
但是没有说明the pooling merchanisom提供何种手段。 那就等好消息了,我后续花一些时间按照Run的方式来写。日志还是沿用MDC.put但提取数据则是从mtc中提取,这样是否可行。 |
@oldratlee, 我使用log4j2对应的MDCAdapter实现。感觉跨线程(包括executorService)也是可以的,但不清楚什么时候是无效的,譬如上述文档中的may not be always be passed to worker thread. 作为建议还是希望工程自身提供一个近似MDC的帮助类,这样我可以不去管mtc的创建,只需要与其进行操作即可。 |
无效时候,应该就是 PS: 这是不停地给我派活儿啊 😓 😄 |
@oldratlee,这不是我的本意啊,不过你也是大牛了。俗话说得好送佛送到西,我刚才表述的想必你也能理解了。如果能进一步的话,至少 MTC可以作为MDC的非官方的缺省实现,既可以满足日志需求也可以满足上下文传递所需要的其他场景。之所以要用MDC的日志,是因为我们的平台有做日志归集的需求,需要通过类似syslog appender这样的流将本地的日志上传到远端比如FLUME。而改动应用最小的又能现有结合比较好的就只有日志LOGGER(而slf4j则当前用得比较广泛)。以MDC来比较MTC的一些测试类,当然MDC更直观(里面是一些静态方法,直接操作就OK了),想必这也是MTC可以进一步可便利化的地方,拜托了。 |
客气了~ 目前 这样的解法,使用者 另外,从你的说明来看,传递
这点详见文档使用Java Agent来修饰JDK线程池实现类。 这点往往需要定制的容器,这样方便大家统一都加上上面的
|
@bwzhang2011 改了这个实现,后续的跟进 你了解一下实现,自己来吧 😄 PS:
|
@oldratlee, 非常感谢持续的改进和推动。如有问题或其他建议,再在那个patch上跟进和沟通。 |
@bwzhang2011 用起来了?可以反馈一下问题解决的如何 :) |
@oldratlee, 可以用起来。感觉跟直接用MDC差不不大,能否直接替代掉ThreadContext中的ThreadContextMap的实现。 |
整个过程用起来还是有些麻烦,给力!:+1:
直接改log4j2的实现,而不是改slf4j-log4j-impl,是吧? 这样避免直接用log4j的代码 有问题。 |
@oldratlee, 抱歉昨天忘记看回复了。是的,诚如你说的。如果能在实现中替代掉应该是比较直接的方式,可否增加mtc-extension的工程,如果可如此替代的话。以log4j2中依赖 https://github.com/apache/logging-log4j2/blob/master/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java ;可否通过将mtc的threadLocal移植到该了中,然后通过agent类来进行替代。 |
@oldratlee, 不知道上述想法是否可行,还是会存在问题。如果adapter中实现的部分均是靠threadContextMap的threadLocal关联的map来操作,若替代成MTC的threadlocal是否能解决问题。可避免新的工程,不知是否可行。 |
@oldratlee, 我再仔细REVIEW了LOG4J2中的ThreaContext代码。其中初始化的部分: final String threadContextMapName = managerProps.getStringProperty(THREAD_CONTEXT_KEY);
final ClassLoader cl = ProviderUtil.findClassLoader();
if (threadContextMapName != null) {
try {
final Class<?> clazz = cl.loadClass(threadContextMapName);
if (ThreadContextMap.class.isAssignableFrom(clazz)) {
contextMap = (ThreadContextMap) clazz.newInstance();
}
} catch (final ClassNotFoundException cnfe) {
LOGGER.error("Unable to locate configured ThreadContextMap {}", threadContextMapName);
} catch (final Exception ex) {
LOGGER.error("Unable to create configured ThreadContextMap {}", threadContextMapName, ex);
}
} 也就是可以设置一个机遇MTC的ThreadContextMap实现即可——由应用来决定是否(只是面向LOG4J2的)。现在我遇到一个问题就是初始化的部分,即LOG4J2的缺省实现为: new InheritableThreadLocal<Map<String, String>>() {
@Override
protected Map<String, String> childValue(final Map<String, String> parentValue) {
return parentValue != null && isMapEnabled //
? Collections.unmodifiableMap(new HashMap<String, String>(parentValue)) //
: null;
}
}; 上述是在childValue中构建一个map。但看过你在patch工程部分中的Log4jMDCAdapter的部分 MtContextThreadLocal<Map<String, String>> log4j2Context = new MtContextThreadLocal<Map<String, String>>() {
@Override
protected void beforeExecute() {
final Map<String, String> log4j2Context = get();
for (Map.Entry<String, String> entry : log4j2Context.entrySet()) {
ThreadContext.put(entry.getKey(), entry.getValue());
}
}
@Override
protected void afterExecute() {
ThreadContext.clearAll();
}
@Override
protected Map<String, String> initialValue() {
return new HashMap<String, String>();
}
}; 貌似没有去覆盖childValue的部分。我想确认的是如果重新构建一个基于MTC的localMap初始化的部分该如何编写。 |
@oldratlee,我到那个patch上提一个issue了:https://github.com/oldratlee/log4j-slf4j-impl-patch-mtc/issues/1 看能否提供一个你的官方实现。 |
不好意思,现在才回复你。
按你的思路,实现了 https://github.com/oldratlee/log4j2-mtc-thread-context-map 把这个依赖放到项目中,即可开启 |
@oldratlee, 能否进一步提供
multi-thread-context
针对slf4j MDCAdapter
的集成支持。MDCAdapter
是slf4j spi
的定义,logback
以及log4j2
等均提供了实现。但个人感觉里面的跨线程上下文传递应该没有
multi-thread-context
考虑得那么完备(也能否帮忙看一下LOG4J2
内部的实现能否满足multi-thread-context
实现的那样),鉴于日志场景中许多事基于slf4j
的(不想MDC
的地方调用了一次,然后又调用一次multi-thread-context
中threadLocal
方式),看能否提供一个这样的替代实现(或者通过java instrument api
替代掉原有的MDCAdapter
实现--可由multi-thread-context
实现)。这个实现能缺省提供则有保障(个人只是有这个思路)The text was updated successfully, but these errors were encountered: