【问题标题】:How can I transfer a stacktrace betwen Scala ExecutionContexts?如何在 Scala ExecutionContext 之间传输堆栈跟踪?
【发布时间】:2020-06-01 06:21:18
【问题描述】:

我围绕 Scala ExecutionContext 编写了一个小型实用程序包装器,以实现跨 Future 实例的 MDC 上下文传输。它按预期工作,但一个不受欢迎的副作用是我们现在似乎没有得到穿过Futures 的堆栈跟踪。如何确保堆栈跟踪与 MDC 一起传播?

这是我的参考代码:

import org.slf4j.MDC

import scala.concurrent.ExecutionContext
import scala.jdk.CollectionConverters._


object MdcOps {

  implicit class ExecutionContextExt(value: ExecutionContext) {
    def withMdc: ExecutionContext = new MdcExecutionContext(value)
  }

  def withMdc[A](mdc: Map[String, Any], replace: Boolean)(doIt: => A): A = {
    val currentMdcContext = getMdc
    val newMdc = if(replace) mdc else mdc ++ currentMdcContext
    try { setMdc(newMdc); doIt }
    finally { setMdc(currentMdcContext) }
  }

  def setMdc(mdc: Map[String, Any]): Unit = {
    if(mdc.isEmpty) {
      MDC.clear()
    } else
      MDC.setContextMap(mdc.view.mapValues(_.toString).toMap.asJava)
  }

  def getMdc: Map[String, String] = Option(MDC.getCopyOfContextMap).map(_.asScala.toMap).getOrElse(Map.empty)
}

class MdcExecutionContext(underlying: ExecutionContext, context: Map[String, String] = Map.empty) extends ExecutionContext {
  override def prepare(): ExecutionContext = new MdcExecutionContext(underlying, MdcOps.getMdc)
  override def execute(runnable: Runnable): Unit = underlying.execute { () =>
    MdcOps.withMdc(context, replace = true)(runnable.run())
  }
  override def reportFailure(t: Throwable): Unit = underlying.reportFailure(t)
}

【问题讨论】:

    标签: scala mdc


    【解决方案1】:

    一般来说是困难。 ZIO 实现了它 - 请参阅幻灯片 53 中的 https://www.slideshare.net/jdegoes/error-management-future-vs-zio 以查看结果 - 但堆栈的创建和传递它会导致 performance penalty x2.4

    由于您无法将 API 更改为例如使用宏在编译时生成堆栈跟踪(使用例如sourcode 库),在每个.map/.flatMap 上,您必须创建一个堆栈跟踪,从中删除不相关的帧,也许将它与来自先前上下文的帧结合起来在MDC中设置。抛开技术细节不谈 - 这很难得到正确和沉重的重量,更像是整个图书馆的材料,而不是简单的实用程序。如果它可以便宜地完成,这将是一个内置。

    如果你对它非常感兴趣,要么选择 ZIO,要么分析它是如何实现的——据我所知,它需要付出很多努力并将这个功能构建到库中,即使这样也会导致性能下降。我只能想象通过使用 JVM 堆栈跟踪并将它们传递过来的惩罚会更大。

    【讨论】:

    • 不是我希望的答案,但仍然感谢!是否可以通过某种方式捕获“内部”异常并使用“外部”堆栈跟踪将它们包装在异常中来解决它?
    • 我前段时间尝试过处理这个问题 - 它基本上要求我:在我自己的 .map.flatMap 上的 Future/IO 包装器中从 sourcecode 获取隐式,然后使用它丰富了某些上下文引发的异常。但我对这种方法有一些严重的怀疑,我不能说我会推荐其他人使用它。
    猜你喜欢
    • 1970-01-01
    • 2011-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-02
    • 2015-05-04
    • 2011-05-25
    相关资源
    最近更新 更多