在java中使用play framewok和akka映射诊断上下文日志记录
我正在尝试用java将mdc登录到PlayFilter中,以处理我在scala中遵循本教程并尝试转换为java的所有请求 但mdc仍然没有传播到所有执行上下文。 我使用这个dispatcher作为默认的调度器,但是它有很多执行上下文。我需要将mdc传播到所有执行上下文 下面是我的java代码在java中使用play framewok和akka映射诊断上下文日志记录,java,scala,playframework,akka,typesafe,Java,Scala,Playframework,Akka,Typesafe,我正在尝试用java将mdc登录到PlayFilter中,以处理我在scala中遵循本教程并尝试转换为java的所有请求 但mdc仍然没有传播到所有执行上下文。 我使用这个dispatcher作为默认的调度器,但是它有很多执行上下文。我需要将mdc传播到所有执行上下文 下面是我的java代码 import java.util.Map; import org.slf4j.MDC; import scala.concurrent.ExecutionContext; import scala.co
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, throughputDeadlineTime,
configureExecutor(), shutDownDuration);
}
public MessageDispatcher dispatcher() {
return instance;
}
}
import java.util.Map;
导入org.slf4j.MDC;
导入scala.concurrent.ExecutionContext;
导入scala.concurrent.duration.duration;
导入scala.concurrent.duration.FiniteDuration;
导入akka.dispatch.Dispatcher;
导入akka.dispatch.ExecutorServiceFactoryProvider;
导入akka.dispatch.MessageDispatcherConfiguration;
公共类MDCPropagatingDispatcher扩展了Dispatcher{
公共MDCPropagatingDispatcher(
MessageDispatcherConfiguration\u配置程序,字符串id,
int吞吐量,持续时间通过输出死线时间,
ExecutorServiceFactoryProvider ExecutorServiceFactoryProvider,
有限持续时间(关闭超时){
超级(\u配置程序、id、吞吐量、吞吐量死线时间、,
executorServiceFactoryProvider,关闭超时);
}
@凌驾
public ExecutionContext prepare(){
最终映射mdcContext=MDC.getCopyOfContextMap();
返回新的ExecutionContext(){
@凌驾
public void execute(可运行的r){
Map oldMDCContext=MDC.getCopyOfContextMap();
setContextMap(mdcContext);
试一试{
r、 run();
}最后{
setContextMap(oldMDCContext);
}
}
@凌驾
public ExecutionContext prepare(){
归还这个;
}
@凌驾
公共失效报告失败(可丢弃的t){
play.Logger.info(“dispatcher中发生错误”);
}
};
}
私有void setContextMap(映射上下文){
if(上下文==null){
MDC.clear();
}否则{
play.Logger.info(“set context”+context.toString());
setContextMap(上下文);
}
}
}
导入java.util.concurrent.TimeUnit;
导入scala.concurrent.duration.duration;
导入scala.concurrent.duration.FiniteDuration;
导入com.typesafe.config.config;
导入akka.dispatch.DispatcherPrerequisites;
导入akka.dispatch.MessageDispatcher;
导入akka.dispatch.MessageDispatcherConfiguration;
公共类MDCPropagatingDispatcherConfiguration扩展
MessageDispatcherConfiguration{
私有消息调度实例;
公共MDCPropagatingDispatcherConfiguration(配置,
DispatcherPrerequire(站点先决条件){
超级(配置,先决条件);
Duration throughputDeadlineTime=新完成的持续时间(-1,
时间单位(毫秒);
最终持续时间关闭持续时间=新的最终持续时间(1,
时间单位(毫秒);
instance=new-MDCPropagatingDispatcher(这是“play.akka.actor.context.play-filter-context”,
100,通过输出死线时间,
configureExecutor(),关机持续时间);
}
public MessageDispatcher(){
返回实例;
}
}
过滤拦截器
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();
}
};
}
公共类MdcLogFilter实现EssentialFilter{
@凌驾
公共要素申请(下一步为最终要素){
返回新的MdcLogAction(){
@凌驾
公共迭代者申请(
最终请求头(请求头){
最后一个字符串uuid=Utils.generateRandomUUID();
MDC.put(“uuid”,uuid);
play.Logger.info(“请求已启动”+uuid);
final ExecutionContext playFilterContext=Akka.system()
.调度员()
.lookup(“play.akka.actor.context.play自定义筛选上下文”);
返回next.apply(requestHeader.map)(
新的AbstractFunction1(){
@凌驾
公共SimpleResult应用(SimpleResult SimpleResult){
play.Logger.info(“请求结束”+uuid);
MDC.移除(“uuid”);
返回simpleResult;
}
},playFilterContext);
}
@凌驾
公共要素应用(){
返回next.apply();
}
};
}
}下面是我的解决方案,在现实生活中得到了验证。它在Scala中,不是为了玩,而是为了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。我通过依赖注入实现这一点,但有不同的方法。我就是这样做的:
这种方法背后的思想如下。MDC对属性及其值使用线程本地存储。如果您的单个请求可以在多个线程上运行,那么您需要确保启动的新线程使用正确的MDC。为此,创建一个自定义执行器,确保在新线程开始执行分配给它的任务之前,将MDC值正确复制到新线程中。您还必须确保当线程完成任务并继续执行其他任务时,您将旧值放入其MDC中,因为池中的线程可以在不同的请求之间切换 你能解决它吗?如果是,你能发布你的解决方案吗
bind[ExecutionContext] idBy BindingIds.GlobalExecutionContext toSingle {
MDCHttpExecutionContext.fromExecutionContextWithCurrentMDC(
ExecutionContext.fromExecutorService(
Executors.newFixedThreadPool(globalThreadPoolSize)
)
)
}