2
2

More than 5 years have passed since last update.

Play2(Scala)でMDCを使う

Last updated at Posted at 2014-07-14

ほぼ参考にしたここのままなので原文を読んだ方が早いです。

CustomExecutionContextを定義

原文のままだとコンパイルできなかったので、SimpleResult -> Result, Map[_, _] -> Map[String, String]に変えています。

package concurrent

import org.slf4j.MDC
import scala.concurrent.{ ExecutionContextExecutor, ExecutionContext }

/**
 * slf4j provides a MDC [[http://logback.qos.ch/manual/mdc.html Mapped Diagnostic Context]]
 * based on a [[ThreadLocal]]. In an asynchronous environment, the callbacks can be called
 * in another thread, where the local thread variable does not exist anymore.
 *
 * This execution context fixes this problem:
 * it propagates the MDC from the caller's thread to the callee's one.
 */
object MDCHttpExecutionContext {

    /**
     * Create an MDCHttpExecutionContext with values from the current thread.
     */
    def fromThread(delegate: ExecutionContext): ExecutionContextExecutor =
        new MDCHttpExecutionContext(MDC.getCopyOfContextMap, delegate)

}

/**
 * Manages execution to ensure that the given MDC context are set correctly
 * in the current thread. Actual execution is performed by a delegate ExecutionContext.
 */
class MDCHttpExecutionContext(mdcContext: java.util.Map[String, String], delegate: ExecutionContext) extends ExecutionContextExecutor {
    def execute(runnable: Runnable) = delegate.execute(new Runnable {
        def run() {
            val oldMDCContext = MDC.getCopyOfContextMap
            setContextMap(mdcContext)
            try {
                runnable.run()
            } finally {
                setContextMap(oldMDCContext)
            }
        }
    })

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

    def reportFailure(t: Throwable) = delegate.reportFailure(t)
}

アプリケーションで使うデフォルトのExecutionContextを定義

package concurrent

import scala.concurrent.ExecutionContext

/**
 * The standard [[play.api.libs.concurrent.Execution.defaultContext]] loses the MDC context.
 *
 * This custom [[ExecutionContext]] propagates the MDC context, so that the request
 * and the correlation IDs can be logged.
 */
object Execution {

    object Implicits {
        implicit def defaultContext: ExecutionContext = Execution.defaultContext
    }

    def defaultContext: ExecutionContext = MDCHttpExecutionContext.fromThread(play.api.libs.concurrent.Execution.defaultContext)

}

あとは

import play.api.libs.concurrent.Execution.Implicits.defaultContext

を使っているところを

import concurrent.Execution.Implicits._

に変える。

CustomExecutionContextを使うためにActionを定義

package controllers

import concurrent.Execution
import scala.concurrent.Future
import play.api.mvc.Result
import scala.concurrent.ExecutionContext
import play.api.mvc.ActionBuilder
import play.api.mvc.Request

object Action extends ActionBuilder[Request] {
    def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
        block(request)
    }

    /**
     * The standard [[play.api.mvc.Action]] loses the MDC context.
     *
     * This action builder sets the [[ExecutionContext]] so that the
     * MDC context is propagated.
     * With this custom [[ExecutionContext]], the request and the correlation IDs
     * can be logged.
     */
    override def executionContext: ExecutionContext = Execution.defaultContext
}

あとはこのActionを使ってControllerを記述する。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2