All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kamon.instrumentation.logback.LogbackInstrumentation.scala Maven / Gradle / Ivy

There is a newer version: 2.7.4
Show newest version
/*
 * Copyright 2013-2021 The Kamon Project 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package kamon.instrumentation.logback

import com.typesafe.config.Config
import kamon.Kamon
import kamon.context.Context
import kamon.context.Storage.Scope
import kamon.instrumentation.context.HasContext
import kamon.tag.Tag
import kamon.trace.{Identifier, Span}
import kanela.agent.api.instrumentation.InstrumentationBuilder
import kanela.agent.libs.net.bytebuddy.asm.Advice

import java.util
import scala.collection.JavaConverters._


class LogbackInstrumentation extends InstrumentationBuilder {

  onSubTypesOf("ch.qos.logback.core.spi.DeferredProcessingAware")
    .mixin(classOf[HasContext.MixinWithInitializer])

  onType("ch.qos.logback.core.spi.AppenderAttachableImpl")
    .advise(method("appendLoopOnAppenders"), AppendLoopOnAppendersAdvice)

  onType("ch.qos.logback.classic.util.LogbackMDCAdapter")
    .advise(method("getPropertyMap"), classOf[MdcPropertyMapAdvice])
}

object LogbackInstrumentation {

  @volatile private var _settings = readSettings(Kamon.config())
  Kamon.onReconfigure(newConfig => _settings = readSettings(newConfig))

  def settings(): Settings =
    _settings

  case class Settings(
    propagateContextToMDC: Boolean,
    mdcTraceIdKey: String,
    mdcSpanIdKey: String,
    mdcSpanOperationNameKey: String,
    mdcCopyTags: Boolean,
    mdcCopyKeys: Seq[String]
  )

  private def readSettings(config: Config): Settings = {
    val logbackConfig = config.getConfig("kamon.instrumentation.logback")

    Settings(
      logbackConfig.getBoolean("mdc.copy.enabled"),
      logbackConfig.getString("mdc.trace-id-key"),
      logbackConfig.getString("mdc.span-id-key"),
      logbackConfig.getString("mdc.span-operation-name-key"),
      logbackConfig.getBoolean("mdc.copy.tags"),
      logbackConfig.getStringList("mdc.copy.entries").asScala.toSeq
    )
  }
}

object AppendLoopOnAppendersAdvice {

  @Advice.OnMethodEnter
  def enter(@Advice.Argument(0) event: Any): Scope =
    Kamon.storeContext(event.asInstanceOf[HasContext].context)

  @Advice.OnMethodExit
  def exit(@Advice.Enter scope: Scope): Unit =
    scope.close()
}

object ContextToMdcPropertyMapAppender {

  def appendContext(mdc: java.util.Map[String, String]): java.util.Map[String, String] = {
    val settings = LogbackInstrumentation.settings()

    if (settings.propagateContextToMDC && mdc != null) {
      val currentContext = Kamon.currentContext()
      val span = currentContext.get(Span.Key)
      val mdcWithKamonContext = new util.HashMap[String, String](mdc)

      if (span.trace.id != Identifier.Empty) {
        mdcWithKamonContext.put(settings.mdcTraceIdKey, span.trace.id.string)
        mdcWithKamonContext.put(settings.mdcSpanIdKey, span.id.string)
        mdcWithKamonContext.put(settings.mdcSpanOperationNameKey, span.operationName())
      }

      if (settings.mdcCopyTags) {
        currentContext.tags.iterator().foreach(t => {
          mdcWithKamonContext.put(t.key, Tag.unwrapValue(t).toString)
        })
      }

      settings.mdcCopyKeys.foreach { key =>
        currentContext.get(Context.key[Any](key, "")) match {
          case Some(value) if value.toString.nonEmpty => mdcWithKamonContext.put(key, value.toString)
          case keyValue if keyValue != null && keyValue.toString.nonEmpty => mdcWithKamonContext.put(key, keyValue.toString)
          case _ => // Just ignore the nulls and empty strings
        }
      }

      mdcWithKamonContext
    } else {
      mdc
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy