gpt4 book ai didi

scala - 跨线程跟踪上下文特定数据

转载 作者:行者123 更新时间:2023-12-04 17:52:37 25 4
gpt4 key购买 nike

我在 Play 中就知道了!使用 Scala 时没有 Http.context 可用,因为这个想法是利用隐式来传递堆栈周围的任何数据。但是,当您需要一条可用于整个上下文的信息时,这似乎需要通过很多样板。

更具体地说,我感兴趣的是跟踪从请求 header 传递的 UUID 并使其可供任何记录器使用,以便每个请求都有自己的唯一标识符。我希望这对调用记录器(或日志包装器)的任何人都是无缝的

来自 .NET 背景的 http 上下文通过异步调用流动,这也可以通过 WCF 中的调用上下文实现。此时,您可以向记录器注册一个函数,以根据类似“%requestID%”的记录模式返回请求的当前 uuid。

构建更大的分布式系统,您需要能够跨多个堆栈关联请求。

但是,作为 scala 和 play 的新手,我什至不确定去哪里寻找一种方法来做到这一点?

最佳答案

您在 Java 中寻找的称为映射诊断上下文或 MDC(至少由 SLF4J 定义)- here's an article I found that details how to set this up for Play .为了为 future 的访问者保留详细信息,这里是用于 MDC 传播 Akka 调度程序的代码:

package monitoring

import java.util.concurrent.TimeUnit

import akka.dispatch._
import com.typesafe.config.Config
import org.slf4j.MDC

import scala.concurrent.ExecutionContext
import scala.concurrent.duration.{Duration, FiniteDuration}

/**
* Configurator for a MDC propagating dispatcher.
* Authored by Yann Simon
* See: http://yanns.github.io/blog/2014/05/04/slf4j-mapped-diagnostic-context-mdc-with-play-framework/
*
* To use it, configure play like this:
* {{{
* play {
* akka {
* actor {
* default-dispatcher = {
* type = "monitoring.MDCPropagatingDispatcherConfigurator"
* }
* }
* }
* }
* }}}
*
* Credits to James Roper for the [[https://github.com/jroper/thread-local-context-propagation/ initial implementation]]
*/
class MDCPropagatingDispatcherConfigurator(config: Config, prerequisites: DispatcherPrerequisites)
extends MessageDispatcherConfigurator(config, prerequisites) {

private val instance = new MDCPropagatingDispatcher(
this,
config.getString("id"),
config.getInt("throughput"),
FiniteDuration(config.getDuration("throughput-deadline-time", TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS),
configureExecutor(),
FiniteDuration(config.getDuration("shutdown-timeout", TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS))

override def dispatcher(): MessageDispatcher = instance
}

/**
* A MDC propagating dispatcher.
*
* This dispatcher propagates the MDC current request context if it's set when it's executed.
*/
class MDCPropagatingDispatcher(_configurator: MessageDispatcherConfigurator,
id: String,
throughput: Int,
throughputDeadlineTime: Duration,
executorServiceFactoryProvider: ExecutorServiceFactoryProvider,
shutdownTimeout: FiniteDuration)
extends Dispatcher(_configurator, id, throughput, throughputDeadlineTime, executorServiceFactoryProvider, shutdownTimeout ) {

self =>

override def prepare(): ExecutionContext = new ExecutionContext {
// capture the MDC
val mdcContext = MDC.getCopyOfContextMap

def execute(r: Runnable) = self.execute(new Runnable {
def run() = {
// backup the callee MDC context
val oldMDCContext = MDC.getCopyOfContextMap

// Run the runnable with the captured context
setContextMap(mdcContext)
try {
r.run()
} finally {
// restore the callee MDC context
setContextMap(oldMDCContext)
}
}
})
def reportFailure(t: Throwable) = self.reportFailure(t)
}

private[this] def setContextMap(context: java.util.Map[String, String]) {
if (context == null) {
MDC.clear()
} else {
MDC.setContextMap(context)
}
}

}

然后您可以使用 MDC.put 在 MDC 中设置值并使用 MDC.remove 删除它(或者,如果您需要从一组同步调用中添加和删除一些上下文,请查看 putCloseable):

import org.slf4j.MDC

// Somewhere in a handler
MDC.put("X-UserId", currentUser.id)

// Later, when the user is no longer available
MDC.remove("X-UserId")

并使用 %mdc{field-name:default-value} 将它们添加到您的日志输出中:

<!-- an example from the blog -->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %coloredLevel %logger{35} %mdc{X-UserId:--} - %msg%n%rootException</pattern>
</encoder>
</appender>

还有更多详情in the linked blog post关于调整 Play 用来正确传播 MDC 上下文的 ExecutionContext(作为替代方法)。

关于scala - 跨线程跟踪上下文特定数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28306429/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com