Scala Akka 使用 SLF4J MDC 进行日志记录

标签 scala akka slf4j mdc

我正在配置我的 Akka 应用程序以使用此处指定的 SLF4J 记录器:

http://doc.akka.io/docs/akka/2.3.4/scala/logging.html

在引擎盖下,我依靠 Logback 来进行日志记录。我正在开发一个用于记录目的的通用模块,用户可以在其参与者系统中使用该模块。主要是,我正在创造一个他们可以混合的特征。

我有一个特点:

我有这样的东西:

trait ActorLogger {

    val log: DiagnosticLoggingAdapter = Logging(this);

}

我有一些额外的逻辑会将 MDC 值添加到 DiagnosticLoggingAdapter 的 MDC。
现在的问题是:如果用户想要混入他们的非参与者类,我会完全公开一个不同的记录器。所以我可能有这样的事情:
trait ClassLogger {

    val log = LoggerFactory getLogger getClass.getName
}

我希望 MDC 值能够延续到此记录器。例如,如果我将 MDC 值放入我的 DiagnosticAdapterLogger,我应该期望能够从 org.slf4j.MDC 中获取这些值

如何以干净的方式实现这一目标?

谢谢!

最佳答案

如果你在actor系统之外的所有代码都是单线程的(即你没有产生任何额外的 future 或线程),那么有一个比@jasop 引用更简单的解决方案。

我有这个 mixin 负责填充内部和外部 Actor 的 MDC:

import akka.actor.DiagnosticActorLogging
import akka.contrib.pattern.ReceivePipeline
import org.slf4j.MDC
import scala.collection.JavaConverters.mapAsJavaMapConverter

trait MdcActorLogging extends DiagnosticActorLogging {
  this: ReceivePipeline =>

  /**
    * This is for logging in Akka actors.
    */
  override def mdc(message: Any): akka.event.Logging.MDC = {
    message match {
      case MyMessage(requestId) => Map("requestId" -> requestId)
      case _ => Map()
    }
  }

  /**
    * This makes the MDC accessible for logging outside of Akka actors by wrapping the actor's
    * `receive` method.
    * Implements the [[http://doc.akka.io/docs/akka/2.4/contrib/receive-pipeline.html ReceivePipeline]]
    * pattern.
    */
  pipelineOuter {
    case e @ MyMessage(requestId) =>
      val origContext = MDC.getCopyOfContextMap
      val mdcWithPath = Map("requestId" -> requestId,
        // inside actors this is already provided, but outside we have to add this manually
        "akkaSource" -> self.path.toString)
      MDC.setContextMap(mdcWithPath.asJava)
      ReceivePipeline.Inner(evt) // invoke actual actor logic
        .andAfter {
          if (origContext != null)
            MDC.setContextMap(origContext)
          else
            MDC.clear()
        }
    case e => ReceivePipeline.Inner(e) // pass through
  }
}

非参与者代码可以使用任何记录器,例如混入com.typesafe.scalalogging.LazyLogging特征。

关于Scala Akka 使用 SLF4J MDC 进行日志记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25135043/

相关文章:

java - 使用 Java API 对数据集中所有列进行数据操作

Scala:取消应用带有空值的元组时出现奇怪的MatchError

java - 如何上传和解析csv文件

gradle - Kotlin 1.3+打破SLF4J/log4j

java - 从不同的第三库中排除 slf4j

Scala 宏 : Convert/parse a Tree to a Name

scala - 如何在光滑表映射中省略案例类字段?

java - Akka actor 被另一个运行 CPU 密集型作业的 actor 阻止

concurrency - 基础知识 |线程与响应式(Reactive)并发模型

java - SLF4J-Log4J 似乎没有禁用日志记录