Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/391.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何将MDC与线程池一起使用?_Java_Logging_Slf4j_Logback_Mdc - Fatal编程技术网

Java 如何将MDC与线程池一起使用?

Java 如何将MDC与线程池一起使用?,java,logging,slf4j,logback,mdc,Java,Logging,Slf4j,Logback,Mdc,在我们的软件中,我们广泛用于跟踪web请求的会话ID和用户名等内容。当在原始线程中运行时,这可以正常工作 然而,有很多事情需要在后台处理。为此,我们使用java.concurrent.ThreadPoolExecutor和java.util.Timer类以及一些自滚异步执行服务。所有这些服务都管理自己的线程池 这就是在这样的环境中使用MDC应该说的: 工作线程不能总是从启动线程继承映射的诊断上下文的副本。当java.util.concurrent.Executors用于线程管理时就是这种情况。例

在我们的软件中,我们广泛用于跟踪web请求的会话ID和用户名等内容。当在原始线程中运行时,这可以正常工作

然而,有很多事情需要在后台处理。为此,我们使用
java.concurrent.ThreadPoolExecutor
java.util.Timer
类以及一些自滚异步执行服务。所有这些服务都管理自己的线程池

这就是在这样的环境中使用MDC应该说的:

工作线程不能总是从启动线程继承映射的诊断上下文的副本。当java.util.concurrent.Executors用于线程管理时就是这种情况。例如,newCachedThreadPool方法创建ThreadPoolExecutor,与其他线程池代码一样,它具有复杂的线程创建逻辑

在这种情况下,建议在将任务提交给执行者之前,在原始(主)线程上调用MDC.getCopyOfContextMap()。当任务运行时,作为其第一个操作,它应该调用MDC.setContextMapValues()以将原始MDC值的存储副本与新的Executor托管线程相关联

这很好,但是很容易忘记添加这些呼叫,并且在为时已晚之前没有简单的方法来识别问题。使用Log4j的唯一标志是日志中缺少MDC信息,而使用Logback则会得到过时的MDC信息(因为线程池中的线程继承了其上运行的第一个任务的MDC)。两者都是生产系统中的严重问题


我不认为我们的情况有任何特殊之处,但我在网上找不到关于这个问题的很多信息。显然,这不是很多人会碰到的事情,所以必须有办法避免它。我们做错了什么?我们遇到了类似的问题。您可能需要扩展ThreadPoolExecutor并重写before/afterExecute方法,以便在启动/停止新线程之前进行所需的MDC调用。

是的,这也是我遇到的一个常见问题。有一些解决方法(如所述,手动设置),但理想情况下,您需要一个

  • 一致地设置MDC
  • 避免在MDC不正确但您不知道的情况下出现默认错误;及
  • 尽量减少对线程池使用方式的更改(例如,使用
    MyCallable
    Callable
    进行子类化,或者使用类似的丑陋方式)
这里有一个解决方案,我使用它来满足这三个需求。代码应该是自解释的

(作为旁注,可以创建此执行器并将其输入Guava的
MoreExecutors.ListingDecorator()
,如果 您使用番石榴的
ListanableFuture

import org.slf4j.MDC;
导入java.util.Map;
导入java.util.concurrent.*;
/**
*与SLF4J MDC兼容的{@link ThreadPoolExecutor}。
*

*通常,MDC用于在每个线程变量中存储诊断信息(例如用户的会话id),以便于 *日志记录。然而,尽管MDC数据被传递给线程子级,但当线程在系统中被重用时,这不起作用 *线程池。这是{@link ThreadPoolExecutor}在每个任务之前适当设置MDC数据的替换。 *

*由jlevy创建。 *日期:2013年6月14日 */ 公共类MdcThreadPoolExecutor扩展了ThreadPoolExecutor{ 最终私有布尔useFixedContext; 最终私有映射固定上下文; /** *任务线程从提交线程获取MDC的池。 */ 公共静态MdcThreadPoolExecutor newWithInheritedMdc(int corePoolSize、int maximumPoolSize、long keepAliveTime、, 时间单位,阻塞队列(工作队列){ 返回新的MdcThreadPoolExecutor(null、corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue); } /** *任务线程从创建池的线程获取固定MDC的池。 */ @抑制警告(“未选中”) 公共静态MdcThreadPoolExecutor newWithCurrentMdc(int corePoolSize、int maximumPoolSize、long keepAliveTime、, 时间单位,阻塞队列(工作队列){ 返回新的MdcThreadPoolExecutor(MDC.getCopyOfContextMap()、corePoolSize、maximumPoolSize、keepAliveTime、unit、, 工作队列); } /** *任务线程始终具有指定的固定MDC的池。 */ 公共静态MdcThreadPoolExecutor newWithFixedMdc(映射fixedContext,int-corePoolSize, int maximumPoolSize,long keepAliveTime,TimeUnit, 阻塞队列(工作队列){ 返回新的MdcThreadPoolExecutor(fixedContext、corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue); } 私有MdcThreadPoolExecutor(映射fixedContext、int-corePoolSize、int-maximumPoolSize、, long keepAliveTime、时间单位、阻塞队列(工作队列){ super(corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue); this.fixedContext=fixedContext; useFixedContext=(fixedContext!=null); } @抑制警告(“未选中”) 私有映射getContextFortTask(){ 返回useFixedContext?fixedContext:MDC.getCopyOfContextMap(); } /** *所有执行都将注入MDC。{@code ThreadPoolExecutor}的提交方法({@code submit()}等) *所有人都同意这一点。 */ @凌驾 public void execute(Runnable命令){ execute(wrap(命令,getContextForTask()); } 公共静态可运行包装(最终可运行,最终映射上下文){ 返回新的Runnable(){ @凌驾 公开募捐{ Map previous=MDC.getCopyOfContextMap(); 如果(上下文)

import org.slf4j.MDC;

import java.util.Map;
import java.util.concurrent.*;

/**
 * A SLF4J MDC-compatible {@link ThreadPoolExecutor}.
 * <p/>
 * In general, MDC is used to store diagnostic information (e.g. a user's session id) in per-thread variables, to facilitate
 * logging. However, although MDC data is passed to thread children, this doesn't work when threads are reused in a
 * thread pool. This is a drop-in replacement for {@link ThreadPoolExecutor} sets MDC data before each task appropriately.
 * <p/>
 * Created by jlevy.
 * Date: 6/14/13
 */
public class MdcThreadPoolExecutor extends ThreadPoolExecutor {

    final private boolean useFixedContext;
    final private Map<String, Object> fixedContext;

    /**
     * Pool where task threads take MDC from the submitting thread.
     */
    public static MdcThreadPoolExecutor newWithInheritedMdc(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                                                            TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        return new MdcThreadPoolExecutor(null, corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    /**
     * Pool where task threads take fixed MDC from the thread that creates the pool.
     */
    @SuppressWarnings("unchecked")
    public static MdcThreadPoolExecutor newWithCurrentMdc(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                                                          TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        return new MdcThreadPoolExecutor(MDC.getCopyOfContextMap(), corePoolSize, maximumPoolSize, keepAliveTime, unit,
                workQueue);
    }

    /**
     * Pool where task threads always have a specified, fixed MDC.
     */
    public static MdcThreadPoolExecutor newWithFixedMdc(Map<String, Object> fixedContext, int corePoolSize,
                                                        int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                                                        BlockingQueue<Runnable> workQueue) {
        return new MdcThreadPoolExecutor(fixedContext, corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    private MdcThreadPoolExecutor(Map<String, Object> fixedContext, int corePoolSize, int maximumPoolSize,
                                  long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        this.fixedContext = fixedContext;
        useFixedContext = (fixedContext != null);
    }

    @SuppressWarnings("unchecked")
    private Map<String, Object> getContextForTask() {
        return useFixedContext ? fixedContext : MDC.getCopyOfContextMap();
    }

    /**
     * All executions will have MDC injected. {@code ThreadPoolExecutor}'s submission methods ({@code submit()} etc.)
     * all delegate to this.
     */
    @Override
    public void execute(Runnable command) {
        super.execute(wrap(command, getContextForTask()));
    }

    public static Runnable wrap(final Runnable runnable, final Map<String, Object> context) {
        return new Runnable() {
            @Override
            public void run() {
                Map previous = MDC.getCopyOfContextMap();
                if (context == null) {
                    MDC.clear();
                } else {
                    MDC.setContextMap(context);
                }
                try {
                    runnable.run();
                } finally {
                    if (previous == null) {
                        MDC.clear();
                    } else {
                        MDC.setContextMap(previous);
                    }
                }
            }
        };
    }
}
static public Map<String, String> mdcContextMap = MDC.getCopyOfContextMap();
MDC.setContextMap(Application.mdcContextMap);
ExecutorService executor = Executors.newFixedThreadPool(4);
Map<String, String> mdcContextMap = MDC.getCopyOfContextMap();
executor.submit(() -> {
    MDC.setContextMap(mdcContextMap);
    // my stuff
});
private final class LoggingTaskDecorator implements TaskDecorator {

    @Override
    public Runnable decorate(Runnable task) {
        // web thread
        Map<String, String> webThreadContext = MDC.getCopyOfContextMap();
        return () -> {
            // work thread
            try {
                // TODO: is this thread safe?
                MDC.setContextMap(webThreadContext);
                task.run();
            } finally {
                MDC.clear();
            }
        };
    }

}