org.slf4j.MDC Scala Examples

The following examples show how to use org.slf4j.MDC. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example.
Example 1
Source File: TraceFriendlyExecutionContextExecutor.scala    From money   with Apache License 2.0 5 votes vote down vote up
package com.comcast.money.core.concurrent

import com.comcast.money.core.internal.{ MDCSupport, SpanLocal }
import com.comcast.money.core.logging.TraceLogging
import org.slf4j.MDC

import scala.concurrent.{ ExecutionContext, ExecutionContextExecutor }

class TraceFriendlyExecutionContextExecutor(wrapped: ExecutionContext)
  extends ExecutionContextExecutor with TraceLogging {

  lazy val mdcSupport = new MDCSupport()

  override def execute(task: Runnable): Unit = {
    val inherited = SpanLocal.current
    val submittingThreadsContext = MDC.getCopyOfContextMap

    wrapped.execute(
      new Runnable {
        override def run = {
          mdcSupport.propogateMDC(Option(submittingThreadsContext))
          SpanLocal.clear()
          inherited.foreach(SpanLocal.push)
          try {
            task.run
          } catch {
            case t: Throwable =>
              logException(t)
              throw t
          } finally {
            SpanLocal.clear()
            MDC.clear()
          }
        }
      })
  }

  override def reportFailure(t: Throwable): Unit = wrapped.reportFailure(t)
}

object TraceFriendlyExecutionContextExecutor {
  object Implicits {
    implicit lazy val global: TraceFriendlyExecutionContextExecutor = new TraceFriendlyExecutionContextExecutor(scala.concurrent.ExecutionContext.global)
  }

  def apply(ec: ExecutionContext) = new TraceFriendlyExecutionContextExecutor(ec)
} 
Example 2
Source File: ActorBootstrap.scala    From vamp   with Apache License 2.0 5 votes vote down vote up
package io.vamp.common.akka

import akka.actor.{ ActorRef, ActorSystem, PoisonPill }
import akka.util.Timeout
import com.typesafe.scalalogging.Logger
import io.vamp.common.{ ClassProvider, Namespace }
import org.slf4j.{ LoggerFactory, MDC }

import scala.concurrent.Future
import scala.reflect.{ ClassTag, classTag }

trait Bootstrap extends BootstrapLogger {

  def start(): Future[Unit] = Future.successful(())

  def stop(): Future[Unit] = Future.successful(())
}

trait ActorBootstrap extends BootstrapLogger {

  private var actors: Future[List[ActorRef]] = Future.successful(Nil)

  def createActors(implicit actorSystem: ActorSystem, namespace: Namespace, timeout: Timeout): Future[List[ActorRef]]

  def start(implicit actorSystem: ActorSystem, namespace: Namespace, timeout: Timeout): Future[Unit] = {
    info(s"Starting ${getClass.getSimpleName}")
    actors = createActors(actorSystem, namespace, timeout)
    actors.map(_ ⇒ ())(actorSystem.dispatcher)
  }

  def restart(implicit actorSystem: ActorSystem, namespace: Namespace, timeout: Timeout): Future[Unit] = {
    stop.flatMap(_ ⇒ start)(actorSystem.dispatcher)
  }

  def stop(implicit actorSystem: ActorSystem, namespace: Namespace): Future[Unit] = {
    info(s"Stopping ${getClass.getSimpleName}")
    actors.map(_.reverse.foreach(_ ! PoisonPill))(actorSystem.dispatcher)
  }

  def alias[T: ClassTag](name: String, default: String ⇒ Future[ActorRef])(implicit actorSystem: ActorSystem, namespace: Namespace, timeout: Timeout): Future[ActorRef] = {
    ClassProvider.find[T](name).map { clazz ⇒
      IoC.alias(classTag[T].runtimeClass, clazz)
      IoC.createActor(clazz)
    } getOrElse default(name)
  }
}

trait BootstrapLogger {

  protected val logger = Logger(LoggerFactory.getLogger(getClass))

  protected def info(message: String)(implicit namespace: Namespace): Unit = {
    MDC.put("namespace", namespace.name)
    try logger.info(message) finally MDC.remove("namespace")
  }
} 
Example 3
Source File: AbstractAppender.scala    From rollbar-scala   with MIT License 5 votes vote down vote up
package com.storecove.rollbar.appenders

import com.storecove.rollbar.util.FiniteQueue
import com.storecove.rollbar.{RollbarNotifier, RollbarNotifierDefaults, RollbarNotifierFactory}
import org.slf4j.MDC

import scala.collection.JavaConversions._
import scala.collection.{immutable, mutable}


trait AbstractAppender {

    protected val DEFAULT_LOGS_LIMITS = 100

    protected var enabled: Boolean = true
    protected var onlyThrowable: Boolean = true

    protected var url: String = RollbarNotifierDefaults.defaultUrl
    protected var apiKey: String = _
    protected var environment: String = _
    protected var notifyLevelString: String = "ERROR"
    protected var limit: Int = DEFAULT_LOGS_LIMITS

    protected val rollbarNotifier: RollbarNotifier = RollbarNotifierFactory.getNotifier(apiKey, environment)

    protected val logBuffer: FiniteQueue[String] = new FiniteQueue[String](immutable.Queue[String]())

    def setNotifyLevel(level: String): Unit

    protected def notifyLevel: Any = "ERROR"

    def setEnabled(enabled: Boolean): Unit = this.enabled = enabled

    def setOnlyThrowable(onlyThrowable: Boolean): Unit = this.onlyThrowable = onlyThrowable

    def setApiKey(apiKey: String): Unit = {
        this.apiKey = apiKey
        rollbarNotifier.setApiKey(apiKey)
    }

    def setEnvironment(environment: String): Unit = {
        this.environment = environment
        rollbarNotifier.setEnvironment(environment)
    }

    def setUrl(url: String): Unit = {
        this.url = url
        rollbarNotifier.setUrl(url)
    }

    def setLimit(limit: Int): Unit = this.limit = limit

    def getEnabled: Boolean = enabled
    def getOnlyThrowable: Boolean = onlyThrowable
    def getApiKey: String = apiKey
    def getEnvironment: String = environment
    def getUrl: String = url
    def getNotifyLevel: String = notifyLevelString
    def getLimit: Int = limit

    protected def getMDCContext: mutable.Map[String, String] = {
        val mdc = MDC.getCopyOfContextMap
        if (mdc == null) {
            mutable.Map.empty[String, String]
        } else {
            mapAsScalaMap(mdc)
        }
    }

} 
Example 4
Source File: MdcLoggingExecutionContext.scala    From http-verbs   with Apache License 2.0 5 votes vote down vote up
package uk.gov.hmrc.play.http.logging

import org.slf4j.MDC
import play.api.libs.concurrent.Execution.defaultContext
import uk.gov.hmrc.http.logging.LoggingDetails

import scala.collection.JavaConverters._
import scala.concurrent.{ExecutionContext, Future}

@deprecated("MdcLoggingExecutionContext no longer required, please inject Play's default EC instead", "8.9.0")
object MdcLoggingExecutionContext {
  implicit def fromLoggingDetails(implicit loggingDetails: LoggingDetails): ExecutionContext =
    new MdcLoggingExecutionContext(defaultContext, loggingDetails.mdcData)
}

class MdcLoggingExecutionContext(wrapped: ExecutionContext, mdcData: Map[String, String]) extends ExecutionContext {

  def execute(runnable: Runnable) {
    wrapped.execute(new RunWithMDC(runnable, mdcData))
  }

  private class RunWithMDC(runnable: Runnable, mdcData: Map[String, String]) extends Runnable {
    def run(): Unit = {
      mdcData.foreach {
        case (k, v) => MDC.put(k, v)
      }
      try {
        runnable.run()
      } finally {
        MDC.clear()
      }
    }
  }

  def reportFailure(t: Throwable): Unit = wrapped.reportFailure(t)
}

object Mdc {

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

  def withMdc[A](block: => Future[A], mdcData: Map[String, String])(implicit ec: ExecutionContext): Future[A] =
    block.map { a =>
      mdcData.foreach {
        case (k, v) => MDC.put(k, v)
      }
      a
    }(ec)

  
  def preservingMdc[A](block: => Future[A])(implicit ec: ExecutionContext): Future[A] =
    withMdc(block, mdcData)
} 
Example 5
Source File: SLF4JSpec.scala    From scribe   with MIT License 5 votes vote down vote up
package spec

import java.util.TimeZone

import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
import org.slf4j.{LoggerFactory, MDC}
import scribe.handler.LogHandler
import scribe.output.LogOutput
import scribe.util.Time
import scribe.writer.Writer
import scribe.{Level, LogRecord, Logger}

class SLF4JSpec extends AnyWordSpec with Matchers {
  TimeZone.setDefault(TimeZone.getTimeZone("UTC"))

  private var logs: List[LogRecord[_]] = Nil
  private var logOutput: List[String] = Nil
  private val recordHolder = LogHandler.default.withMinimumLevel(Level.Info).withWriter(new Writer {
    override def write[M](record: LogRecord[M], output: LogOutput): Unit = {
      logs = record :: logs
      logOutput = output.plainText :: logOutput
    }
  })

  "SLF4J" should {
    "set the time to an arbitrary value" in {
      Time.function = () => 1542376191920L
    }
    "remove existing handlers from Root" in {
      Logger.root.clearHandlers().replace()
    }
    "add a testing handler" in {
      Logger.root.withHandler(recordHolder).replace()
    }
    "verify not records are in the RecordHolder" in {
      logs.isEmpty should be(true)
    }
    "log to Scribe" in {
      val logger = LoggerFactory.getLogger(getClass)
      logger.info("Hello World!")
    }
    "verify Scribe received the record" in {
      logs.size should be(1)
      val r = logs.head
      r.level should be(Level.Info)
      r.message.plainText should be("Hello World!")
      r.className should be("spec.SLF4JSpec")
      logs = Nil
    }
    "verify Scribe wrote value" in {
      logOutput.size should be(1)
      val s = logOutput.head
      s should be("2018.11.16 13:49:51 [INFO] spec.SLF4JSpec - Hello World!")
    }
    "use MDC" in {
      MDC.put("name", "John Doe")
      val logger = LoggerFactory.getLogger(getClass)
      logger.info("A generic name")
      logOutput.head should be("2018.11.16 13:49:51 [INFO] spec.SLF4JSpec - A generic name (name: John Doe)")
    }
    "clear MDC" in {
      MDC.clear()
      val logger = LoggerFactory.getLogger(getClass)
      logger.info("MDC cleared")
      logOutput.head should be("2018.11.16 13:49:51 [INFO] spec.SLF4JSpec - MDC cleared")
    }
    "make sure logging nulls doesn't error" in {
      val logger = LoggerFactory.getLogger(getClass)
      logger.error(null)
      logs.length should be(3)
      logOutput.head should be("2018.11.16 13:49:51 [ERROR] spec.SLF4JSpec - null")
    }
  }
} 
Example 6
Source File: SLF4JSpec.scala    From scribe   with MIT License 5 votes vote down vote up
package spec

import java.util.TimeZone

import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
import org.slf4j.{LoggerFactory, MDC}
import scribe.handler.LogHandler
import scribe.output.LogOutput
import scribe.util.Time
import scribe.writer.Writer
import scribe.{Level, LogRecord, Logger}

class SLF4JSpec extends AnyWordSpec with Matchers {
  TimeZone.setDefault(TimeZone.getTimeZone("UTC"))

  private var logs: List[LogRecord[_]] = Nil
  private var logOutput: List[String] = Nil
  private val recordHolder = LogHandler.default.withMinimumLevel(Level.Info).withWriter(new Writer {
    override def write[M](record: LogRecord[M], output: LogOutput): Unit = {
      logs = record :: logs
      logOutput = output.plainText :: logOutput
    }
  })

  "SLF4J" should {
    "set the time to an arbitrary value" in {
      Time.function = () => 1542376191920L
    }
    "remove existing handlers from Root" in {
      Logger.root.clearHandlers().replace()
    }
    "add a testing handler" in {
      Logger.root.withHandler(recordHolder).replace()
    }
    "verify not records are in the RecordHolder" in {
      logs.isEmpty should be(true)
    }
    "log to Scribe" in {
      val logger = LoggerFactory.getLogger(getClass)
      logger.info("Hello World!")
    }
    "verify Scribe received the record" in {
      logs.size should be(1)
      val r = logs.head
      r.level should be(Level.Info)
      r.message.plainText should be("Hello World!")
      r.className should be("spec.SLF4JSpec")
      logs = Nil
    }
    "verify Scribe wrote value" in {
      logOutput.size should be(1)
      val s = logOutput.head
      s should be("2018.11.16 13:49:51 [INFO] spec.SLF4JSpec - Hello World!")
    }
    "use MDC" in {
      MDC.put("name", "John Doe")
      val logger = LoggerFactory.getLogger(getClass)
      logger.info("A generic name")
      logOutput.head should be("2018.11.16 13:49:51 [INFO] spec.SLF4JSpec - A generic name (name: John Doe)")
    }
    "clear MDC" in {
      MDC.clear()
      val logger = LoggerFactory.getLogger(getClass)
      logger.info("MDC cleared")
      logOutput.head should be("2018.11.16 13:49:51 [INFO] spec.SLF4JSpec - MDC cleared")
    }
    "make sure logging nulls doesn't error" in {
      val logger = LoggerFactory.getLogger(getClass)
      logger.error(null)
      logs.length should be(3)
      logOutput.head should be("2018.11.16 13:49:51 [ERROR] spec.SLF4JSpec - null")
    }
  }
} 
Example 7
Source File: TraceFriendlyExecutionContextExecutorSpec.scala    From money   with Apache License 2.0 5 votes vote down vote up
package com.comcast.money.core.concurrent

import com.comcast.money.api.SpanId
import com.comcast.money.core.SpecHelpers
import com.comcast.money.core.internal.SpanLocal
import org.mockito.Mockito._
import org.scalatest.mockito.MockitoSugar
import org.scalatest.{ BeforeAndAfterEach, Matchers, OneInstancePerTest, WordSpec }
import org.slf4j.MDC

import scala.concurrent.duration._
import scala.concurrent.{ Await, ExecutionContext, Future }

class TraceFriendlyExecutionContextExecutorSpec extends WordSpec
  with Matchers
  with MockitoSugar
  with OneInstancePerTest
  with ConcurrentSupport
  with SpecHelpers
  with BeforeAndAfterEach {

  import com.comcast.money.core.concurrent.TraceFriendlyExecutionContextExecutor.Implicits.global

  override def beforeEach() = {
    SpanLocal.clear()
    MDC.clear()
  }

  // brings in the implicit executor

  "TraceFriendlyExecutionContext" should {
    "propagate the current trace local value" in {
      val originalSpanId = new SpanId("1", 2L, 3L)
      val originalSpan = testSpan(originalSpanId)
      SpanLocal.push(originalSpan)

      val future = Future {
        SpanLocal.current.get.info.id
      }

      val futureResult = Await.result(future, 100 millis)
      futureResult shouldEqual originalSpanId
    }
    "propagate no span value if none is present" in {
      SpanLocal.clear()

      val future = Future {
        SpanLocal.current
      }

      val futureResult = Await.result(future, 100 millis)
      futureResult shouldEqual None
    }
    "propagate only the latest span id value" in {
      val spanId1 = new SpanId()
      val spanId2 = new SpanId()
      SpanLocal.push(testSpan(spanId1))
      SpanLocal.push(testSpan(spanId2))

      val future = Future {
        SpanLocal.current.get.info.id
      }

      val futureResult = Await.result(future, 100 millis)
      futureResult shouldEqual spanId2
    }
    "delegate reportFailure to the wrapped executor" in {
      val mockExecutionContext = mock[ExecutionContext]
      val traceFriendly = TraceFriendlyExecutionContextExecutor(mockExecutionContext)
      val failure = new IllegalArgumentException()

      traceFriendly.reportFailure(failure)
      verify(mockExecutionContext).reportFailure(failure)
    }
    "propogate MDC data" in {
      MDC.put("FINGERPRINT", "print")
      val future = Future {
        MDC.get("FINGERPRINT")
      }
      MDC.get("FINGERPRINT") shouldEqual "print"
      Await.result(future, 100 millis) shouldEqual "print"
    }

    "Child MDC should not escape to parent " in {
      val future = Future {
        MDC.put("FINGERPRINT", "print")
        MDC.get("FINGERPRINT")
      }
      MDC.get("FINGERPRINT") shouldBe null
      Await.result(future, 100 millis) shouldEqual "print"
    }
  }
} 
Example 8
Source File: TraceFriendlyThreadPoolExecutorSpec.scala    From money   with Apache License 2.0 5 votes vote down vote up
package com.comcast.money.core.concurrent

import java.util.concurrent.{ Callable, ExecutorService }

import com.comcast.money.api.SpanId
import com.comcast.money.core.SpecHelpers
import com.comcast.money.core.internal.SpanLocal
import org.scalatest.mockito.MockitoSugar
import org.scalatest.{ Matchers, OneInstancePerTest, WordSpecLike }
import org.slf4j.MDC

class TraceFriendlyThreadPoolExecutorSpec
  extends WordSpecLike
  with MockitoSugar with Matchers with ConcurrentSupport with OneInstancePerTest with SpecHelpers {

  val executor: ExecutorService = TraceFriendlyThreadPoolExecutor.newCachedThreadPool

  "TraceFriendlyThreadPoolExecutor cachedThreadPool" should {
    "propagate the current span local value" in {
      val traceId = new SpanId("1", 2L, 3L)
      SpanLocal.push(testSpan(traceId))

      val future = executor.submit(testCallable)

      future.get shouldEqual Some(traceId)
      SpanLocal.clear()
    }
    "propagate no span value if none is present" in {
      SpanLocal.clear()

      val future = executor.submit(testCallable)

      future.get shouldEqual None
      SpanLocal.current shouldEqual None
    }
    "propagate only the current span id value" in {
      val traceId1 = new SpanId()
      val traceId2 = new SpanId()
      SpanLocal.push(testSpan(traceId1))
      SpanLocal.push(testSpan(traceId2))

      val future = executor.submit(testCallable)
      future.get shouldEqual Some(traceId2)
    }
    "propagate MDC" in {
      val traceId = new SpanId("1", 2L, 3L)
      SpanLocal.push(testSpan(traceId))
      MDC.put("foo", "bar")

      val mdcCallable = new Callable[String] {
        override def call(): String = MDC.get("foo")
      }

      val future = executor.submit(mdcCallable)

      future.get shouldEqual "bar"
      SpanLocal.clear()
    }
  }
  "TraceFriendlyThreadPoolExecutor fixedThreadPool" should {
    val threadPool: TraceFriendlyThreadPoolExecutor = TraceFriendlyThreadPoolExecutor.newFixedThreadPool(1)
      .asInstanceOf[TraceFriendlyThreadPoolExecutor]

    "created the pool with the specified number of threads" in {
      threadPool.getCorePoolSize shouldEqual 1
    }
  }
} 
Example 9
Source File: SpanLocalSpec.scala    From money   with Apache License 2.0 5 votes vote down vote up
package com.comcast.money.core.internal

import com.comcast.money.api.SpanId
import com.comcast.money.core.handlers.TestData
import org.scalatest.mockito.MockitoSugar
import org.scalatest.{ OneInstancePerTest, BeforeAndAfterEach, Matchers, WordSpec }
import org.slf4j.MDC

class SpanLocalSpec extends WordSpec
  with Matchers with OneInstancePerTest with BeforeAndAfterEach with MockitoSugar with TestData {

  override def afterEach() = {
    SpanLocal.clear()
  }

  "SpanLocal" when {
    "an item exists in span local" should {
      "return the span local value" in {
        SpanLocal.push(testSpan)
        SpanLocal.current shouldEqual Some(testSpan)
      }
      "clear the stored value" in {
        SpanLocal.push(testSpan)

        SpanLocal.clear()
        SpanLocal.current shouldEqual None
      }
      "do nothing if trying to push a null value" in {
        SpanLocal.push(testSpan)
        SpanLocal.push(null)
        SpanLocal.current shouldEqual Some(testSpan)
      }
      "add to the existing call stack" in {
        val nested = testSpan.copy(new SpanId())

        SpanLocal.push(testSpan)
        SpanLocal.push(nested)
        SpanLocal.current shouldEqual Some(nested)
      }
      "pop the last added item from the call stack" in {
        val nested = testSpan.copy(new SpanId())
        SpanLocal.push(testSpan)
        SpanLocal.push(nested)

        val popped = SpanLocal.pop()
        popped shouldEqual Some(nested)
        SpanLocal.current shouldEqual Some(testSpan)
      }
      "set the MDC value on push" in {
        SpanLocal.push(testSpan)

        MDC.get("moneyTrace") shouldEqual MDCSupport.format(testSpan.id)
        MDC.get("spanName") shouldEqual testSpan.name
      }
      "remove the MDC value on pop" in {
        SpanLocal.push(testSpan)
        SpanLocal.pop()

        MDC.get("moneyTrace") shouldBe null
        MDC.get("spanName") shouldBe null
      }
      "reset the MDC value on pop" in {
        SpanLocal.push(testSpan)
        SpanLocal.push(childSpan)

        MDC.get("moneyTrace") shouldEqual MDCSupport.format(childSpan.id)
        MDC.get("spanName") shouldEqual childSpan.name

        SpanLocal.pop()

        MDC.get("moneyTrace") shouldEqual MDCSupport.format(testSpan.id)
        MDC.get("spanName") shouldEqual testSpan.name
      }
      "remove the MDC value on clear" in {
        SpanLocal.push(testSpan)

        MDC.get("moneyTrace") shouldEqual MDCSupport.format(testSpan.id)
        MDC.get("spanName") shouldEqual testSpan.name
        SpanLocal.clear()

        MDC.get("moneyTrace") shouldBe null
        MDC.get("spanName") shouldBe null
      }
    }
  }
} 
Example 10
Source File: MDCSupportSpec.scala    From money   with Apache License 2.0 5 votes vote down vote up
package com.comcast.money.core.internal

import com.comcast.money.api.SpanId
import org.scalatest.{ BeforeAndAfterEach, Matchers, OneInstancePerTest, WordSpec }
import org.slf4j.MDC

import scala.collection.JavaConverters._
import scala.collection.mutable

class MDCSupportSpec extends WordSpec with Matchers with BeforeAndAfterEach with OneInstancePerTest {

  val testMDCSupport = new MDCSupport
  val spanId = new SpanId()

  override def beforeEach() = {
    SpanLocal.clear()
  }

  "MDCSupport" should {
    "set the span in MDC when provide" in {
      testMDCSupport.setSpanMDC(Some(spanId))
      MDC.get("moneyTrace") shouldEqual MDCSupport.format(spanId)
    }
    "clear the MDC value when set to None" in {
      testMDCSupport.setSpanMDC(Some(spanId))
      MDC.get("moneyTrace") shouldEqual MDCSupport.format(spanId)

      testMDCSupport.setSpanMDC(None)
      MDC.get("moneyTrace") shouldBe null
    }
    "not be run if tracing is disabled" in {
      val disabled = new MDCSupport(false)
      disabled.setSpanMDC(Some(spanId))
      MDC.get("moneyTrace") shouldBe null
    }
    "not propogate MDC if disabled" in {
      val mdcContext: mutable.Map[_, _] = mutable.HashMap("FINGERPRINT" -> "print")
      val disabled = new MDCSupport(false)
      disabled.propogateMDC(Some(mdcContext.asJava))
      MDC.get("FINGERPRINT") shouldBe null
    }
    "propogate MDC if not disabled" in {
      val mdcContext: mutable.Map[_, _] = mutable.HashMap("FINGERPRINT" -> "print")

      testMDCSupport.propogateMDC(Some(mdcContext.asJava))
      MDC.get("FINGERPRINT") shouldBe "print"
    }
    "clear MDC if given an empty context" in {
      MDC.put("FINGERPRINT", "print")
      testMDCSupport.propogateMDC(None)
      MDC.get("FINGERPRINT") shouldBe null
    }
    "set span name" in {
      testMDCSupport.setSpanNameMDC(Some("foo"))
      MDC.get("spanName") shouldBe "foo"
      testMDCSupport.getSpanNameMDC shouldBe Some("foo")
    }
    "clear span name from MDC when given an empty value" in {
      MDC.put("spanName", "shouldBeRemoved")
      testMDCSupport.setSpanNameMDC(None)
      MDC.get("spanName") shouldBe null
      testMDCSupport.getSpanNameMDC shouldBe None
    }
  }
} 
Example 11
Source File: StructuredLogSpanHandler.scala    From money   with Apache License 2.0 5 votes vote down vote up
package com.comcast.money.core.handlers

import com.comcast.money.api.{ Note, SpanInfo }
import com.typesafe.config.Config
import org.slf4j.{ Logger, LoggerFactory, MDC }


class StructuredLogSpanHandler(
  val logger: Logger = LoggerFactory.getLogger(classOf[StructuredLogSpanHandler]),
  val mdcFunc: (String, String) => Unit = (x: String, y: String) => MDC.put(x, y))
  extends ConfigurableHandler {

  // Extra constructor because java spring programs have a problem with the default function in the constructor above.
  def this() = this(LoggerFactory.getLogger(classOf[StructuredLogSpanHandler]), (k: String, v: String) => MDC.put(k, v))

  import com.comcast.money.core.handlers.LoggingSpanHandler._

  protected var logFunction: LogFunction = logger.info

  def configure(config: Config): Unit = {

    if (config.hasPath("log-level")) {
      val level = config.getString("log-level").toUpperCase

      // set the log level based on the configured value
      level match {
        case "ERROR" => logFunction = logger.error
        case "WARN" => logFunction = logger.warn
        case "INFO" => logFunction = logger.info
        case "DEBUG" => logFunction = logger.debug
        case "TRACE" => logFunction = logger.trace
      }
    }
  }

  def handle(spanInfo: SpanInfo): Unit = {
    import scala.collection.JavaConverters._
    val baseFields = Seq(
      // The field names below are the same as cedi-dtrace. This makes it easier to query a transaction in elastic search.
      ("trace-id", spanInfo.id.traceId()),
      ("parent-id", spanInfo.id.parentId()),
      ("span-id", spanInfo.id.selfId()),
      ("span-name", spanInfo.name()),
      ("app", spanInfo.appName()),
      ("host", spanInfo.host()),
      ("start-time", java.time.Instant.ofEpochMilli(spanInfo.startTimeMillis())),
      ("end-time", java.time.Instant.ofEpochMilli(spanInfo.endTimeMillis())),
      ("span-duration", spanInfo.durationMicros()),
      ("span-success", spanInfo.success()))
    val noteFields: Seq[(String, Any)] = spanInfo.notes.values.asScala.map(n => (n.name(), n.value())).toSeq
    val allFields = baseFields ++ noteFields

    allFields.foreach(p => mdcFunc(p._1, p._2.toString))

    logFunction(allFields.map { case (k, v) => s"$k:$v" }.mkString("[", "][", "]"))
  }
} 
Example 12
Source File: JavaBackend.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.lf.codegen.backend.java

import com.daml.lf.codegen.backend.Backend
import com.daml.lf.codegen.backend.java.inner.{ClassForType, DecoderClass}
import com.daml.lf.codegen.conf.Conf
import com.daml.lf.codegen.{InterfaceTrees, ModuleWithContext, NodeWithContext}
import com.daml.lf.data.Ref.PackageId
import com.daml.lf.iface.Interface
import com.squareup.javapoet._
import com.typesafe.scalalogging.StrictLogging
import org.slf4j.MDC

import scala.concurrent.{ExecutionContext, Future}

private[codegen] object JavaBackend extends Backend with StrictLogging {

  override def preprocess(
      interfaces: Seq[Interface],
      conf: Conf,
      packagePrefixes: Map[PackageId, String])(
      implicit ec: ExecutionContext): Future[InterfaceTrees] = {
    val tree = InterfaceTrees.fromInterfaces(interfaces)
    for ((decoderPkg, decoderClassName) <- conf.decoderPkgAndClass) {
      val templateNames = extractTemplateNames(tree, packagePrefixes)
      val decoderFile = JavaFile
        .builder(
          decoderPkg,
          DecoderClass.generateCode(decoderClassName, templateNames)
        )
        .build()
      decoderFile.writeTo(conf.outputDirectory)
    }
    Future.successful(tree)
  }

  private def extractTemplateNames(
      tree: InterfaceTrees,
      packagePrefixes: Map[PackageId, String]) = {
    val prefixes = packagePrefixes.mapValues(_.stripSuffix("."))
    tree.interfaceTrees.flatMap(_.bfs(Vector[ClassName]()) {
      case (res, module: ModuleWithContext) =>
        val templateNames = module.typesLineages
          .collect {
            case t if t.`type`.typ.exists(_.getTemplate.isPresent) =>
              ClassName.bestGuess(inner.fullyQualifiedName(t.identifier, packagePrefixes))
          }
        res ++ templateNames
      case (res, _) => res
    })
  }

  def process(
      nodeWithContext: NodeWithContext,
      conf: Conf,
      packagePrefixes: Map[PackageId, String])(implicit ec: ExecutionContext): Future[Unit] = {
    val prefixes = packagePrefixes.mapValues(_.stripSuffix("."))
    nodeWithContext match {
      case moduleWithContext: ModuleWithContext if moduleWithContext.module.types.nonEmpty =>
        // this is a DAML module that contains type declarations => the codegen will create one file
        Future {
          logger.info(
            s"Generating code for module ${moduleWithContext.lineage.map(_._1).toSeq.mkString(".")}")
          for (javaFile <- createTypeDefinitionClasses(moduleWithContext, prefixes)) {
            logger.info(
              s"Writing ${javaFile.packageName}.${javaFile.typeSpec.name} to directory ${conf.outputDirectory}")
            javaFile.writeTo(conf.outputDirectory)

          }
        }
      case _ =>
        Future.successful(())
    }
  }

  private def createTypeDefinitionClasses(
      moduleWithContext: ModuleWithContext,
      packagePrefixes: Map[PackageId, String]): Iterable[JavaFile] = {
    MDC.put("packageId", moduleWithContext.packageId)
    MDC.put("packageIdShort", moduleWithContext.packageId.take(7))
    MDC.put("moduleName", moduleWithContext.name)
    val typeSpecs = for {
      typeWithContext <- moduleWithContext.typesLineages
      javaFile <- ClassForType(typeWithContext, packagePrefixes)
    } yield {
      javaFile
    }
    MDC.remove("packageId")
    MDC.remove("packageIdShort")
    MDC.remove("moduleName")
    typeSpecs
  }
} 
Example 13
Source File: TraceFriendlyThreadPoolExecutor.scala    From money   with Apache License 2.0 5 votes vote down vote up
package com.comcast.money.core.concurrent

import java.util.concurrent._

import com.comcast.money.core.internal.{ MDCSupport, SpanLocal }
import com.comcast.money.core.logging.TraceLogging
import org.slf4j.MDC

object TraceFriendlyThreadPoolExecutor {

  
class TraceFriendlyThreadPoolExecutor(corePoolSize: Int, maximumPoolSize: Int, keepAliveTime: Long, unit: TimeUnit,
  workQueue: BlockingQueue[Runnable])
  extends ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue)
  with TraceLogging {

  lazy val mdcSupport = new MDCSupport()

  def this(corePoolSize: Int, maximumPoolSize: Int, keepAliveTime: Long, unit: TimeUnit,
    workQueue: BlockingQueue[Runnable], threadFactory: ThreadFactory,
    rejectedExecutionHandler: RejectedExecutionHandler) = {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue)
    setThreadFactory(threadFactory)
    setRejectedExecutionHandler(rejectedExecutionHandler)
  }

  override def execute(command: Runnable) = {
    val inherited = SpanLocal.current
    val submittingThreadsContext = MDC.getCopyOfContextMap

    super.execute(
      new Runnable {
        override def run = {
          mdcSupport.propogateMDC(Option(submittingThreadsContext))
          SpanLocal.clear()
          inherited.foreach(SpanLocal.push)
          try {
            command.run()
          } catch {
            case t: Throwable =>
              logException(t)
              throw t
          } finally {
            SpanLocal.clear()
            MDC.clear()
          }
        }
      })
  }
} 
Example 14
Source File: MDCSupport.scala    From money   with Apache License 2.0 5 votes vote down vote up
package com.comcast.money.core.internal

import java.util.Map

import com.comcast.money.api.SpanId
import com.comcast.money.core.Money
import org.slf4j.MDC

object MDCSupport {

  val LogFormat = "[ span-id=%s ][ trace-id=%s ][ parent-id=%s ]"

  def format(spanId: SpanId) = LogFormat.format(spanId.selfId, spanId.traceId, spanId.parentId)
}


class MDCSupport(enabled: Boolean = Money.Environment.enabled) {

  private val MoneyTraceKey = "moneyTrace"
  private val SpanNameKey = "spanName"

  def setSpanMDC(spanId: Option[SpanId]): Unit = if (enabled) {
    spanId match {
      case Some(id) => MDC.put(MoneyTraceKey, MDCSupport.format(id))
      case None => MDC.remove(MoneyTraceKey)
    }
  }

  def propogateMDC(submittingThreadsContext: Option[Map[_, _]]): Unit = if (enabled) {
    submittingThreadsContext match {
      case Some(context: Map[String, String]) => MDC.setContextMap(context)
      case None => MDC.clear()
    }
  }

  def setSpanNameMDC(spanName: Option[String]) =
    if (enabled) {
      spanName match {
        case Some(name) => MDC.put(SpanNameKey, name)
        case None => MDC.remove(SpanNameKey)
      }
    }

  def getSpanNameMDC: Option[String] = Option(MDC.get(SpanNameKey))
} 
Example 15
Source File: MethodTracer.scala    From money   with Apache License 2.0 5 votes vote down vote up
package com.comcast.money.core.logging

import java.lang.reflect.Method

import com.comcast.money.annotations.{ Timed, Traced }
import com.comcast.money.core.{ Money, Tracer }
import com.comcast.money.core.async.AsyncNotifier
import com.comcast.money.core.internal.{ MDCSupport, SpanContext, SpanLocal }
import com.comcast.money.core.reflect.Reflections
import org.slf4j.MDC

import scala.util.{ Failure, Success, Try }

trait MethodTracer extends Reflections with TraceLogging {
  val tracer: Tracer = Money.Environment.tracer
  val asyncNotifier: AsyncNotifier = Money.Environment.asyncNotifier
  val mdcSupport: MDCSupport = new MDCSupport()
  val spanContext: SpanContext = SpanLocal

  def traceMethod(method: Method, annotation: Traced, args: Array[AnyRef], proceed: () => AnyRef): AnyRef = {
    val key = annotation.value()

    tracer.startSpan(key)
    recordTracedParameters(method, args, tracer)

    Try { proceed() } match {
      case Success(result) if annotation.async() =>
        traceAsyncResult(method, annotation, result) match {
          case Some(future) =>
            future
          case None =>
            tracer.stopSpan(true)
            result
        }
      case Success(result) =>
        tracer.stopSpan(true)
        result
      case Failure(exception) =>
        logException(exception)
        tracer.stopSpan(exceptionMatches(exception, annotation.ignoredExceptions()))
        throw exception
    }
  }

  def timeMethod(method: Method, annotation: Timed, proceed: () => AnyRef): AnyRef = {
    val key = annotation.value()
    try {
      tracer.startTimer(key)
      proceed()
    } finally {
      tracer.stopTimer(key)
    }
  }

  def traceAsyncResult(
    method: Method,
    annotation: Traced,
    returnValue: AnyRef): Option[AnyRef] = for {

    // resolve an async notification handler that supports the result
    handler <- asyncNotifier.resolveHandler(method.getReturnType, returnValue)

    // pop the current span from the stack as it will not be stopped by the tracer
    span <- spanContext.pop
    // capture the current MDC context to be applied on the callback thread
    mdc = Option(MDC.getCopyOfContextMap)

    result = handler.whenComplete(method.getReturnType, returnValue) { completed =>
      // reapply the MDC onto the callback thread
      mdcSupport.propogateMDC(mdc)

      // determine if the future completed successfully or exceptionally
      val result = completed match {
        case Success(_) => true
        case Failure(exception) =>
          logException(exception)
          exceptionMatches(exception, annotation.ignoredExceptions())
      }

      // stop the captured span with the success/failure flag
      span.stop(result)
      // clear the MDC from the callback thread
      MDC.clear()
    }
  } yield result
} 
Example 16
Source File: LoggerHandlerWithId.scala    From rokku   with Apache License 2.0 5 votes vote down vote up
package com.ing.wbaa.rokku.proxy.handler

import akka.http.scaladsl.model.{ StatusCode, StatusCodes }
import com.ing.wbaa.rokku.proxy.data.RequestId
import com.ing.wbaa.rokku.proxy.metrics.MetricsFactory
import com.ing.wbaa.rokku.proxy.metrics.MetricsFactory._
import com.typesafe.scalalogging.Logger
import org.slf4j.{ LoggerFactory, MDC }

import scala.collection.mutable

class LoggerHandlerWithId {

  @transient
  private lazy val log: Logger =
    Logger(LoggerFactory.getLogger(getClass.getName))

  private val requestIdKey = "request.id"
  private val statusCodeKey = "request.statusCode"

  def debug(message: String, args: Any*)(implicit id: RequestId): Unit = {
    MDC.put(requestIdKey, id.value)
    MDC.put(statusCodeKey, "-")
    log.debug(message, args.asInstanceOf[mutable.WrappedArray[AnyRef]]: _*)
    MDC.remove(requestIdKey)
    MDC.remove(statusCodeKey)
  }

  def info(message: String, args: Any*)(implicit id: RequestId): Unit = {
    MDC.put(requestIdKey, id.value)
    MDC.put(statusCodeKey, "-")
    log.info(message, args.asInstanceOf[mutable.WrappedArray[AnyRef]]: _*)
    MDC.remove(requestIdKey)
    MDC.remove(statusCodeKey)
  }

  def warn(message: String, args: Any*)(implicit id: RequestId, statusCode: StatusCode = StatusCodes.Continue): Unit = {
    MDC.put(requestIdKey, id.value)
    MDC.put(statusCodeKey, statusCode.value)
    if (args.isInstanceOf[mutable.WrappedArray[_]])
      log.warn(message, args.asInstanceOf[mutable.WrappedArray[AnyRef]]: _*)
    else
      log.warn(message, args.asInstanceOf[scala.collection.immutable.$colon$colon[AnyRef]]: _*)
    MDC.remove(requestIdKey)
    MDC.remove(statusCodeKey)
  }

  def error(message: String, args: Any*)(implicit id: RequestId, statusCode: StatusCode = StatusCodes.Continue): Unit = {
    MDC.put(requestIdKey, id.value)
    MDC.put(statusCodeKey, statusCode.value)
    countLogErrors(MetricsFactory.ERROR_REPORTED_TOTAL)
    if (args.isInstanceOf[mutable.WrappedArray[_]])
      log.error(message, args.asInstanceOf[mutable.WrappedArray[AnyRef]]: _*)
    else
      log.error(message, args.asInstanceOf[scala.collection.immutable.$colon$colon[AnyRef]]: _*)
    MDC.remove(requestIdKey)
    MDC.remove(statusCodeKey)
  }
} 
Example 17
Source File: BaseLogProcessor.scala    From wookiee   with Apache License 2.0 5 votes vote down vote up
package com.webtrends.harness.logging

import akka.util.Helpers
import org.slf4j.MDC

private[harness] trait BaseLogProcessor {

  val sourceThread = "sourceThread"
  val sourceTime = "sourceTime"
  val akkaSource = "akkaSource"

  
  protected def format(template: String, arg: Seq[Any]): String = {
    val sb = new java.lang.StringBuilder(64)
    var p = 0
    var rest = template
    while (p < arg.length) {
      val index = rest.indexOf("{}")
      if (index == -1) {
        sb.append(rest).append(" WARNING arguments left: ").append(arg.length - p)
        rest = ""
        p = arg.length
      } else {
        sb.append(rest.substring(0, index)).append(arg(p))
        rest = rest.substring(index + 2)
        p += 1
      }
    }
    sb.append(rest).toString
  }

  @inline
  protected final def withContext(thread: Thread, timestamp: Long, altSource: Option[String])(logStatement: => Unit) {
    if (!thread.getName.equals(Thread.currentThread.getName)) {
      MDC.put(sourceThread, thread.getName)
    }

    MDC.put(akkaSource, if (altSource.isDefined) altSource.get else "")
    MDC.put(sourceTime, Helpers.currentTimeMillisToUTCString(timestamp))

    try logStatement finally {
      MDC.remove(akkaSource)
      MDC.remove(sourceThread)
      MDC.remove(sourceTime)
    }
  }
} 
Example 18
Source File: Slf4jLogger.scala    From zio-logging   with Apache License 2.0 5 votes vote down vote up
package zio.logging.slf4j

import org.slf4j.{ LoggerFactory, MDC }
import zio.internal.Tracing
import zio.internal.stacktracer.Tracer
import zio.internal.stacktracer.ZTraceElement.{ NoLocation, SourceLocation }
import zio.internal.stacktracer.impl.AkkaLineNumbersTracer
import zio.internal.tracing.TracingConfig
import zio.logging.Logging
import zio.logging._
import zio.{ ZIO, ZLayer }

import scala.jdk.CollectionConverters._
object Slf4jLogger {

  private val tracing = Tracing(Tracer.globallyCached(new AkkaLineNumbersTracer), TracingConfig.enabled)

  private def classNameForLambda(lambda: => AnyRef) =
    tracing.tracer.traceLocation(() => lambda) match {
      case SourceLocation(_, clazz, _, _) => Some(clazz)
      case NoLocation(_)                  => None
    }

  private def logger(name: String) =
    ZIO.effectTotal(
      LoggerFactory.getLogger(
        name
      )
    )

  def make(
    logFormat: (LogContext, => String) => String,
    rootLoggerName: Option[String] = None
  ): ZLayer[Any, Nothing, Logging] =
    Logging.make(
      logger = { (context, line) =>
        val loggerName = context.get(LogAnnotation.Name) match {
          case Nil   => classNameForLambda(line).getOrElse("ZIO.defaultLogger")
          case names => LogAnnotation.Name.render(names)
        }
        logger(loggerName).map {
          slf4jLogger =>
            val maybeThrowable = context.get(LogAnnotation.Throwable).orNull
            context.get(LogAnnotation.Level).level match {
              case LogLevel.Off.level   => ()
              case LogLevel.Debug.level => slf4jLogger.debug(logFormat(context, line), maybeThrowable)
              case LogLevel.Trace.level => slf4jLogger.trace(logFormat(context, line), maybeThrowable)
              case LogLevel.Info.level  => slf4jLogger.info(logFormat(context, line), maybeThrowable)
              case LogLevel.Warn.level  => slf4jLogger.warn(logFormat(context, line), maybeThrowable)
              case LogLevel.Error.level => slf4jLogger.error(logFormat(context, line), maybeThrowable)
              case LogLevel.Fatal.level => slf4jLogger.error(logFormat(context, line), maybeThrowable)
            }
        }
      },
      rootLoggerName = rootLoggerName
    )

  
  def makeWithAnnotationsAsMdc(
    mdcAnnotations: List[LogAnnotation[_]],
    logFormat: (LogContext, => String) => String = (_, s) => s,
    rootLoggerName: Option[String] = None
  ): ZLayer[Any, Nothing, Logging] = {
    val annotationNames = mdcAnnotations.map(_.name)

    Logging.make(
      (context, line) => {
        val loggerName = context.get(LogAnnotation.Name) match {
          case Nil   => classNameForLambda(line).getOrElse("ZIO.defaultLogger")
          case names => LogAnnotation.Name.render(names)
        }
        logger(loggerName).map {
          slf4jLogger =>
            val maybeThrowable = context.get(LogAnnotation.Throwable).orNull

            val mdc: Map[String, String] = context.renderContext.filter {
              case (k, _) => annotationNames.contains(k)
            }
            MDC.setContextMap(mdc.asJava)
            context.get(LogAnnotation.Level).level match {
              case LogLevel.Off.level   => ()
              case LogLevel.Debug.level => slf4jLogger.debug(logFormat(context, line), maybeThrowable)
              case LogLevel.Trace.level => slf4jLogger.trace(logFormat(context, line), maybeThrowable)
              case LogLevel.Info.level  => slf4jLogger.info(logFormat(context, line), maybeThrowable)
              case LogLevel.Warn.level  => slf4jLogger.warn(logFormat(context, line), maybeThrowable)
              case LogLevel.Error.level => slf4jLogger.error(logFormat(context, line), maybeThrowable)
              case LogLevel.Fatal.level => slf4jLogger.error(logFormat(context, line), maybeThrowable)
            }
            MDC.clear()
        }

      },
      rootLoggerName = rootLoggerName
    )
  }
} 
Example 19
Source File: MDCPropagatingDispatcherConfigurator.scala    From daf   with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
package it.gov.daf.common.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}


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
    //val parent = Thread.currentThread().getId

    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)
        //println(s"setto ${Thread.currentThread().getId} - $mdcContext - from $parent")
        try {
          r.run()
        } finally {
          // restore the callee MDC context

          setContextMap(oldMDCContext)
          //println(s"ripristino ${Thread.currentThread().getId} - $oldMDCContext - from $parent")
        }
      }
    })
    def reportFailure(t: Throwable) = self.reportFailure(t)
  }

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

} 
Example 20
Source File: TrackLineageSpec.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.lf.codegen.backend.java.inner

import org.scalatest.{FlatSpec, Matchers}
import org.slf4j.MDC

final class TrackLineageSpec extends FlatSpec with Matchers {

  behavior of "TrackLineage.of"

  it should "work as expected in the plain case" in {
    TrackLineage.of("someEntity", "someName") {
      MDC.get("entityType") shouldEqual "someEntity"
      MDC.get("entityName") shouldEqual "someName"
    }
  }

  it should "work as expected in the nested case" in {
    TrackLineage.of("someEntity", "someName") {
      TrackLineage.of("someNestedEntity", "someNestedName") {
        MDC.get("entityType") shouldEqual "someNestedEntity"
        MDC.get("entityName") shouldEqual "someName.someNestedName"
      }
    }
  }

  it should "correctly pop items when exiting scopes" in {
    TrackLineage.of("someEntity", "someName") {
      MDC.get("entityType") shouldEqual "someEntity"
      MDC.get("entityName") shouldEqual "someName"
      TrackLineage.of("someNestedEntity", "someNestedName") {
        MDC.get("entityType") shouldEqual "someNestedEntity"
        MDC.get("entityName") shouldEqual "someName.someNestedName"
      }
      MDC.get("entityType") shouldEqual "someEntity"
      MDC.get("entityName") shouldEqual "someName"
    }
    MDC.get("entityType") shouldBe null
    MDC.get("entityName") shouldBe null
  }

  it should "correctly track lineages on a per-thread basis" in {
    val t1 = new Thread(() => {
      for (_ <- 1 to 10000) {
        TrackLineage.of("someEntity1", "someName1") {
          MDC.get("entityType") shouldEqual "someEntity1"
          MDC.get("entityName") shouldEqual "someName1"
        }
        Thread.`yield`()
        MDC.get("entityType") shouldBe null
        MDC.get("entityName") shouldBe null
      }
    })
    val t2 = new Thread(() => {
      for (_ <- 1 to 10000) {
        TrackLineage.of("someEntity2", "someName2") {
          MDC.get("entityType") shouldEqual "someEntity2"
          MDC.get("entityName") shouldEqual "someName2"
        }
        Thread.`yield`()
        MDC.get("entityType") shouldBe null
        MDC.get("entityName") shouldBe null
      }
    })
    t1.start()
    t2.start()
    t1.join()
    t2.join()
  }

  it should "work correctly in the face of exceptions" in {
    TrackLineage.of("someEntity", "someName") {
      try {
        TrackLineage.of("someNestedEntity", "someNestedName") {
          throw new RuntimeException
        }
      } catch {
        case _: Exception => // expected, do nothing
          MDC.get("entityType") shouldEqual "someEntity"
          MDC.get("entityName") shouldEqual "someName"
      }
    }
  }

} 
Example 21
Source File: TrackLineage.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.lf.codegen.backend.java.inner

import java.util.{ArrayDeque, Deque}

import org.slf4j.MDC

import scala.collection.JavaConverters.iterableAsScalaIterableConverter


private[inner] object TrackLineage {

  private[this] val nameLineage = new ThreadLocal[Deque[String]] {
    override protected def initialValue() = new ArrayDeque[String](16)
  }

  private[this] val typeLineage = new ThreadLocal[Deque[String]] {
    override protected def initialValue() = new ArrayDeque[String](16)
  }

  def of[A](entityType: String, entityName: String)(f: => A): A = {
    enter(entityType, entityName)
    try f
    finally exit()
  }

  private def enter(entityType: String, entityName: String): Unit = synchronized {
    typeLineage.get.addLast(entityType)
    MDC.put("entityType", entityType)
    nameLineage.get.addLast(entityName)
    MDC.put("entityName", nameLineageAsString)
  }

  private def exit(): Unit = synchronized {
    typeLineage.get.removeLast()
    if (typeLineage.get.isEmpty) {
      MDC.remove("entityType")
    } else {
      MDC.put("entityType", typeLineage.get.peekLast)
    }
    nameLineage.get.removeLast()
    if (nameLineage.get.isEmpty) {
      MDC.remove("entityName")
    } else {
      MDC.put("entityName", nameLineageAsString)
    }
  }

  private def nameLineageAsString: String = nameLineage.get.asScala.mkString(".")

}