我正在尝试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,因为来自池的线程可以在不同请求之间切换.