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