微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在Java中使用play framewok和akka进行映射诊断上下文记录

我正在尝试mdc日志记录在 java中的所有请求我在这个教程中遵循 Scala并尝试转换为java http://yanns.github.io/blog/2014/05/04/slf4j-mapped-diagnostic-context-mdc-with-play-framework/

但仍然mdc不传播到所有执行上下文.
我使用这个dispathcher作为认调度程序,但它有很多执行上下文.我需要mdc传播到所有执行上下文

下面是我的java代码

import java.util.Map;

import org.slf4j.MDC;

import scala.concurrent.ExecutionContext;
import scala.concurrent.duration.Duration;
import scala.concurrent.duration.FiniteDuration;
import akka.dispatch.dispatcher;
import akka.dispatch.ExecutorServiceFactoryProvider;
import akka.dispatch.MessagedispatcherConfigurator;

public class MDCPropagatingdispatcher extends dispatcher {
    public MDCPropagatingdispatcher(
            MessagedispatcherConfigurator _configurator,String id,int throughput,Duration throughputDeadlineTime,ExecutorServiceFactoryProvider executorServiceFactoryProvider,FiniteDuration shutdownTimeout) {
        super(_configurator,id,throughput,throughputDeadlineTime,executorServiceFactoryProvider,shutdownTimeout);

    }

    @Override
    public ExecutionContext prepare() {
        final Map<String,String> mdcContext = MDC.getcopyOfContextMap();
        return new ExecutionContext() {

            @Override
            public void execute(Runnable r) {
                Map<String,String> oldMDCContext = MDC.getcopyOfContextMap();
                setContextMap(mdcContext);
                try {
                    r.run();
                } finally {
                    setContextMap(oldMDCContext);
                }
            }

            @Override
            public ExecutionContext prepare() {
                return this;
            }

            @Override
            public void reportFailure(Throwable t) {
                play.Logger.info("error occured in dispatcher");
            }

        };
    }

    private void setContextMap(Map<String,String> context) {
        if (context == null) {
            MDC.clear();
        } else {
            play.Logger.info("set context "+ context.toString());
            MDC.setContextMap(context);
        }
    }
}



import java.util.concurrent.TimeUnit;

import scala.concurrent.duration.Duration;
import scala.concurrent.duration.FiniteDuration;

import com.typesafe.config.Config;

import akka.dispatch.dispatcherPrerequisites;
import akka.dispatch.Messagedispatcher;
import akka.dispatch.MessagedispatcherConfigurator;

public class MDCPropagatingdispatcherConfigurator extends
        MessagedispatcherConfigurator {
    private Messagedispatcher instance;

    public MDCPropagatingdispatcherConfigurator(Config config,dispatcherPrerequisites prerequisites) {
        super(config,prerequisites);
        Duration throughputDeadlineTime = new FiniteDuration(-1,TimeUnit.MILLISECONDS);
        FiniteDuration shutDownDuration = new FiniteDuration(1,TimeUnit.MILLISECONDS);
        instance = new MDCPropagatingdispatcher(this,"play.akka.actor.contexts.play-filter-context",100,configureExecutor(),shutDownDuration);
    }

    public Messagedispatcher dispatcher() {
        return instance;
    }

}

过滤拦截

public class MdcLogFilter implements EssentialFilter {
@Override
public EssentialAction apply(final EssentialAction next) {
    return new MdcLogAction() {
        @Override
        public Iteratee<byte[],SimpleResult> apply(
                final RequestHeader requestHeader) {
            final String  uuid = Utils.generaterandomUUID();
            MDC.put("uuid",uuid);
            play.Logger.info("request started"+uuid);
            final ExecutionContext playFilterContext = Akka.system()
                    .dispatchers()
                    .lookup("play.akka.actor.contexts.play-custom-filter-context");
            return next.apply(requestHeader).map(
                    new AbstractFunction1<SimpleResult,SimpleResult>() {
                        @Override
                        public SimpleResult apply(SimpleResult simpleResult) {
                            play.Logger.info("request ended"+uuid);
                            MDC.remove("uuid");
                            return simpleResult;
                        }
                    },playFilterContext);

        }

        @Override
        public EssentialAction apply() {
            return next.apply();
        }
    };
}

}

解决方法

以下是我的解决方案,在现实生活中证明.它在Scala,而不是Play,而是Scalatra,但其基本概念是一样的.希望您能够找出如何将其移植到Java.
import org.slf4j.MDC
import java.util.{Map => JMap}
import scala.concurrent.{ExecutionContextExecutor,ExecutionContext}

object MDCHttpExecutionContext {

  def fromExecutionContextWithCurrentMDC(delegate: ExecutionContext): ExecutionContextExecutor =
    new MDCHttpExecutionContext(MDC.getcopyOfContextMap(),delegate)
}

class MDCHttpExecutionContext(mdcContext: JMap[String,String],delegate: ExecutionContext)
  extends ExecutionContextExecutor {

  def execute(runnable: Runnable): Unit = {
    val callingThreadMDC = MDC.getcopyOfContextMap()
    delegate.execute(new Runnable {
      def run() {
        val currentThreadMDC = MDC.getcopyOfContextMap()
        setContextMap(callingThreadMDC)
        try {
          runnable.run()
        } finally {
          setContextMap(currentThreadMDC)
        }
      }
    })
  }

  private[this] def setContextMap(context: JMap[String,String]): Unit = {
    Option(context) match {
      case Some(ctx) => {
        MDC.setContextMap(context)
      }
      case None => {
        MDC.clear()
      }
    }
  }

  def reportFailure(t: Throwable): Unit = delegate.reportFailure(t)
}

您必须确保在所有异步调用中使用此ExecutionContext.我通过依赖注入来实现这一点,但有不同的方法.这就是我用subcut做的:

bind[ExecutionContext] idBy BindingIds.GlobalExecutionContext toSingle {
    MDCHttpExecutionContext.fromExecutionContextWithCurrentMDC(
      ExecutionContext.fromExecutorService(
        Executors.newFixedThreadPool(globalThreadPoolSize)
      )
    )
  }

这种做法背后的想法如下. MDC使用线程本地存储来获取属性及其值.如果您的单个请求可以在多个线程上运行,那么您需要确保您启动的新线程使用正确的MDC.为此,您创建一个自定义执行程序,确保在开始执行您分配给它的任务之前将MDC值正确复制到新线程中.您还必须确保当线程完成任务并继续执行其他操作时,将旧值放入其MDC,因为来自池的线程可以在不同请求之间切换.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


应用场景 C端用户提交工单、工单创建完成之后、会发布一条工单创建完成的消息事件(异步消息)、MQ消费者收到消息之后、会通知各处理器处理该消息、各处理器处理完后都会发布一条将该工单写入搜索引擎的消息、最终该工单出现在搜索引擎、被工单处理人检索和处理。 事故异常体现 1、异常体现 从工单的流转记录发现、
线程类,设置有一个公共资源 package cn.org.chris.concurrent; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @Descrip
Java中的数字(带有0前缀和字符串)
在Java 9中使用JLink的目的是什么?
Java Stream API Filter(过滤器)
在Java中找到正数和负数数组元素的数量
Java 9中JShell中的不同启动脚本是什么?
使用Java的位填充错误检测技术
java中string是什么
如何使用Java中的JSON-lib API将Map转换为JSON对象?