aboutsummaryrefslogtreecommitdiff
path: root/kamon-play/src/main/scala/kamon
diff options
context:
space:
mode:
Diffstat (limited to 'kamon-play/src/main/scala/kamon')
-rw-r--r--kamon-play/src/main/scala/kamon/play/Play.scala14
-rw-r--r--kamon-play/src/main/scala/kamon/play/action/KamonTraceActions.scala4
-rw-r--r--kamon-play/src/main/scala/kamon/play/instrumentation/LoggerLikeInstrumentation.scala43
-rw-r--r--kamon-play/src/main/scala/kamon/play/instrumentation/RequestInstrumentation.scala66
-rw-r--r--kamon-play/src/main/scala/kamon/play/instrumentation/WSInstrumentation.scala9
5 files changed, 61 insertions, 75 deletions
diff --git a/kamon-play/src/main/scala/kamon/play/Play.scala b/kamon-play/src/main/scala/kamon/play/Play.scala
index 2184fa84..eb212940 100644
--- a/kamon-play/src/main/scala/kamon/play/Play.scala
+++ b/kamon-play/src/main/scala/kamon/play/Play.scala
@@ -20,7 +20,7 @@ import akka.actor.{ ExtendedActorSystem, Extension, ExtensionId, ExtensionIdProv
import akka.event.Logging
import kamon.Kamon
import kamon.http.HttpServerMetrics
-import kamon.metric.Metrics
+import kamon.metric.Entity
import play.api.libs.ws.WS.WSRequest
import play.api.mvc.RequestHeader
@@ -36,9 +36,15 @@ class PlayExtension(private val system: ExtendedActorSystem) extends Kamon.Exten
log.info(s"Starting the Kamon(Play) extension")
private val config = system.settings.config.getConfig("kamon.play")
+ val httpServerMetrics = {
+ val metricsExtension = Kamon.metrics
+ val factory = metricsExtension.instrumentFactory(HttpServerMetrics.category)
+ val entity = Entity("play-server", HttpServerMetrics.category)
- val httpServerMetrics = Kamon(Metrics)(system).register(HttpServerMetrics, HttpServerMetrics.Factory).get
- val defaultDispatcher = system.dispatchers.lookup(config.getString("dispatcher"))
+ metricsExtension.register(entity, new HttpServerMetrics(factory)).recorder
+ }
+
+ val defaultDispatcher = system.dispatcher
val includeTraceToken: Boolean = config.getBoolean("automatic-trace-token-propagation")
val traceTokenHeaderName: String = config.getString("trace-token-header-name")
@@ -55,6 +61,6 @@ trait PlayNameGenerator {
}
class DefaultPlayNameGenerator extends PlayNameGenerator {
- def generateTraceName(requestHeader: RequestHeader): String = requestHeader.method + ": " + requestHeader.uri
+ def generateTraceName(requestHeader: RequestHeader): String = s"${requestHeader.method}: ${requestHeader.uri}"
def generateHttpClientSegmentName(request: WSRequest): String = request.url
}
diff --git a/kamon-play/src/main/scala/kamon/play/action/KamonTraceActions.scala b/kamon-play/src/main/scala/kamon/play/action/KamonTraceActions.scala
index 0e777fd5..55e39bed 100644
--- a/kamon-play/src/main/scala/kamon/play/action/KamonTraceActions.scala
+++ b/kamon-play/src/main/scala/kamon/play/action/KamonTraceActions.scala
@@ -16,13 +16,13 @@
package kamon.play.action
-import kamon.trace.TraceRecorder
+import kamon.trace.TraceContext
import play.api.mvc._
import scala.concurrent.Future
case class TraceName[A](name: String)(action: Action[A]) extends Action[A] {
def apply(request: Request[A]): Future[SimpleResult] = {
- TraceRecorder.rename(name)
+ TraceContext.currentContext.rename(name)
action(request)
}
lazy val parser = action.parser
diff --git a/kamon-play/src/main/scala/kamon/play/instrumentation/LoggerLikeInstrumentation.scala b/kamon-play/src/main/scala/kamon/play/instrumentation/LoggerLikeInstrumentation.scala
index e2ffd3f9..3c79fae4 100644
--- a/kamon-play/src/main/scala/kamon/play/instrumentation/LoggerLikeInstrumentation.scala
+++ b/kamon-play/src/main/scala/kamon/play/instrumentation/LoggerLikeInstrumentation.scala
@@ -15,19 +15,12 @@
package kamon.play.instrumentation
-import kamon.trace._
+import kamon.trace.logging.MdcKeysSupport
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation._
-import org.slf4j.MDC
-import play.api.LoggerLike
@Aspect
-class LoggerLikeInstrumentation {
-
- import kamon.play.instrumentation.LoggerLikeInstrumentation._
-
- @DeclareMixin("play.api.LoggerLike+")
- def mixinContextAwareToLoggerLike: TraceContextAware = TraceContextAware.default
+class LoggerLikeInstrumentation extends MdcKeysSupport {
@Pointcut("execution(* play.api.LoggerLike+.info(..))")
def infoPointcut(): Unit = {}
@@ -41,35 +34,9 @@ class LoggerLikeInstrumentation {
@Pointcut("execution(* play.api.LoggerLike+.trace(..))")
def tracePointcut(): Unit = {}
- @Around("(infoPointcut() || warnPointcut() || errorPointcut() || tracePointcut()) && this(logger)")
- def aroundLog(pjp: ProceedingJoinPoint, logger: LoggerLike): Any = {
- withMDC {
- pjp.proceed()
- }
- }
-}
-
-object LoggerLikeInstrumentation {
-
- @inline final def withMDC[A](block: ⇒ A): A = {
- val keys = putAndExtractKeys(extractProperties(TraceRecorder.currentContext))
-
- try block finally keys.foreach(k ⇒ MDC.remove(k))
- }
-
- def putAndExtractKeys(values: Iterable[Map[String, Any]]): Iterable[String] = values.map {
- value ⇒ value.map { case (key, value) ⇒ MDC.put(key, value.toString); key }
- }.flatten
-
- def extractProperties(traceContext: TraceContext): Iterable[Map[String, Any]] = traceContext match {
- case ctx: DefaultTraceContext ⇒
- ctx.traceLocalStorage.underlyingStorage.values.collect {
- case traceLocalValue @ (p: Product) ⇒ {
- val properties = p.productIterator
- traceLocalValue.getClass.getDeclaredFields.filter(field ⇒ field.getName != "$outer").map(_.getName -> properties.next).toMap
- }
- }
- case EmptyTraceContext ⇒ Iterable.empty[Map[String, Any]]
+ @Around("(infoPointcut() || warnPointcut() || errorPointcut() || tracePointcut())")
+ def aroundLog(pjp: ProceedingJoinPoint): Any = withMdc {
+ pjp.proceed()
}
}
diff --git a/kamon-play/src/main/scala/kamon/play/instrumentation/RequestInstrumentation.scala b/kamon-play/src/main/scala/kamon/play/instrumentation/RequestInstrumentation.scala
index b44e45a3..d98f6b95 100644
--- a/kamon-play/src/main/scala/kamon/play/instrumentation/RequestInstrumentation.scala
+++ b/kamon-play/src/main/scala/kamon/play/instrumentation/RequestInstrumentation.scala
@@ -17,7 +17,8 @@ package kamon.play.instrumentation
import kamon.Kamon
import kamon.play.{ Play, PlayExtension }
-import kamon.trace.{ TraceContextAware, TraceRecorder }
+import kamon.trace.TraceLocal.{ HttpContextKey, HttpContext }
+import kamon.trace._
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation._
import play.api.mvc._
@@ -26,48 +27,49 @@ import play.libs.Akka
@Aspect
class RequestInstrumentation {
- import RequestInstrumentation.normaliseTraceName
+
+ import RequestInstrumentation._
@DeclareMixin("play.api.mvc.RequestHeader+")
def mixinContextAwareNewRequest: TraceContextAware = TraceContextAware.default
- @After("execution(* play.api.GlobalSettings+.onStart(*)) && args(application)")
- def afterApplicationStart(application: play.api.Application): Unit = {
- Kamon(Play)(Akka.system())
- }
+ @Before("call(* play.api.GlobalSettings.onRouteRequest(..)) && args(requestHeader)")
+ def beforeRouteRequest(requestHeader: RequestHeader): Unit = {
+ import Kamon.tracer
+ val playExtension = Kamon(Play)
- @Before("execution(* play.api.GlobalSettings+.onRouteRequest(..)) && args(requestHeader)")
- def onRouteRequest(requestHeader: RequestHeader): Unit = {
- val system = Akka.system()
- val playExtension = Kamon(Play)(system)
val defaultTraceName = playExtension.generateTraceName(requestHeader)
-
val token = if (playExtension.includeTraceToken) {
requestHeader.headers.toSimpleMap.find(_._1 == playExtension.traceTokenHeaderName).map(_._2)
} else None
- TraceRecorder.start(defaultTraceName, token)(system)
+ val newContext = token.map(t ⇒ tracer.newContext(defaultTraceName, t)).getOrElse(tracer.newContext(defaultTraceName))
+ TraceContext.setCurrentContext(newContext)
}
- @Around("execution(* play.api.GlobalSettings+.doFilter(*)) && args(next)")
+ @Around("call(* play.api.GlobalSettings.doFilter(*)) && args(next)")
def aroundDoFilter(pjp: ProceedingJoinPoint, next: EssentialAction): Any = {
val essentialAction = (requestHeader: RequestHeader) ⇒ {
- // TODO: Move to a Kamon-specific dispatcher.
- val executor = Kamon(Play)(Akka.system()).defaultDispatcher
+
+ val playExtension = Kamon(Play)
+ val executor = playExtension.defaultDispatcher
def onResult(result: SimpleResult): SimpleResult = {
- TraceRecorder.withTraceContextAndSystem { (ctx, system) ⇒
+ TraceContext.map { ctx ⇒
ctx.finish()
- val playExtension = Kamon(Play)(system)
- recordHttpServerMetrics(result.header, ctx.name, playExtension)
+ recordHttpServerMetrics(result.header.status.toString, ctx.name)
if (playExtension.includeTraceToken) result.withHeaders(playExtension.traceTokenHeaderName -> ctx.token)
else result
} getOrElse result
}
+
+ //store in TraceLocal useful data to diagnose errors
+ storeDiagnosticData(requestHeader)
+
//override the current trace name
- normaliseTraceName(requestHeader).map(TraceRecorder.rename)
+ normaliseTraceName(requestHeader).map(TraceContext.currentContext.rename)
// Invoke the action
next(requestHeader).map(onResult)(executor)
@@ -75,21 +77,33 @@ class RequestInstrumentation {
pjp.proceed(Array(EssentialAction(essentialAction)))
}
- @Before("execution(* play.api.GlobalSettings+.onError(..)) && args(request, ex)")
- def beforeOnError(request: TraceContextAware, ex: Throwable): Unit = TraceRecorder.withTraceContextAndSystem { (ctx, system) ⇒
- val playExtension = Kamon(Play)(system)
- playExtension.httpServerMetrics.recordResponse(ctx.name, "500")
+ @Before("call(* play.api.GlobalSettings.onError(..)) && args(request, ex)")
+ def beforeOnError(request: TraceContextAware, ex: Throwable): Unit = {
+ TraceContext.map { ctx ⇒
+ recordHttpServerMetrics("500", ctx.name)
+ }
}
- private def recordHttpServerMetrics(header: ResponseHeader, traceName: String, playExtension: PlayExtension): Unit =
- playExtension.httpServerMetrics.recordResponse(traceName, header.status.toString)
+ def recordHttpServerMetrics(status: String, traceName: String): Unit =
+ Kamon(Play).httpServerMetrics.recordResponse(traceName, status)
+
+ def storeDiagnosticData(request: RequestHeader): Unit = {
+ val agent = request.headers.get(UserAgent).getOrElse(Unknown)
+ val forwarded = request.headers.get(XForwardedFor).getOrElse(Unknown)
+
+ TraceLocal.store(HttpContextKey)(HttpContext(agent, request.uri, forwarded))
+ }
}
object RequestInstrumentation {
- import kamon.metric.Metrics.AtomicGetOrElseUpdateForTriemap
+ import kamon.util.TriemapAtomicGetOrElseUpdate.Syntax
import java.util.Locale
import scala.collection.concurrent.TrieMap
+ val UserAgent = "User-Agent"
+ val XForwardedFor = "X-Forwarded-For"
+ val Unknown = "unknown"
+
private val cache = TrieMap.empty[String, String]
def normaliseTraceName(requestHeader: RequestHeader): Option[String] = requestHeader.tags.get(Routes.ROUTE_VERB).map({ verb ⇒
diff --git a/kamon-play/src/main/scala/kamon/play/instrumentation/WSInstrumentation.scala b/kamon-play/src/main/scala/kamon/play/instrumentation/WSInstrumentation.scala
index fdc7fb09..f1ceb5d3 100644
--- a/kamon-play/src/main/scala/kamon/play/instrumentation/WSInstrumentation.scala
+++ b/kamon-play/src/main/scala/kamon/play/instrumentation/WSInstrumentation.scala
@@ -18,10 +18,9 @@ package kamon.play.instrumentation
import kamon.Kamon
import kamon.play.Play
-import kamon.trace.{ SegmentCategory, SegmentMetricIdentity }
+import kamon.trace.{ TraceContext, SegmentCategory }
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.{ Around, Aspect, Pointcut }
-import kamon.trace.TraceRecorder
import play.api.libs.ws.WS.WSRequest
import scala.concurrent.Future
import play.api.libs.ws.Response
@@ -34,8 +33,8 @@ class WSInstrumentation {
@Around("onExecuteRequest(request)")
def aroundExecuteRequest(pjp: ProceedingJoinPoint, request: WSRequest): Any = {
- TraceRecorder.withTraceContextAndSystem { (ctx, system) ⇒
- val playExtension = Kamon(Play)(system)
+ TraceContext.map { ctx ⇒
+ val playExtension = Kamon(Play)
val executor = playExtension.defaultDispatcher
val segmentName = playExtension.generateHttpClientSegmentName(request)
val segment = ctx.startSegment(segmentName, SegmentCategory.HttpClient, Play.SegmentLibraryName)
@@ -43,6 +42,6 @@ class WSInstrumentation {
response.map(result ⇒ segment.finish())(executor)
response
- } getOrElse (pjp.proceed())
+ } getOrElse pjp.proceed()
}
} \ No newline at end of file