aboutsummaryrefslogtreecommitdiff
path: root/kamon-play/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'kamon-play/src/main')
-rw-r--r--kamon-play/src/main/resources/META-INF/aop.xml1
-rw-r--r--kamon-play/src/main/resources/reference.conf4
-rw-r--r--kamon-play/src/main/scala/kamon/play/Play.scala3
-rw-r--r--kamon-play/src/main/scala/kamon/play/instrumentation/LoggerLikeInstrumentation.scala70
-rw-r--r--kamon-play/src/main/scala/kamon/play/instrumentation/RequestInstrumentation.scala36
-rw-r--r--kamon-play/src/main/scala/kamon/play/instrumentation/WSInstrumentation.scala5
6 files changed, 102 insertions, 17 deletions
diff --git a/kamon-play/src/main/resources/META-INF/aop.xml b/kamon-play/src/main/resources/META-INF/aop.xml
index ca499a33..e24d48d5 100644
--- a/kamon-play/src/main/resources/META-INF/aop.xml
+++ b/kamon-play/src/main/resources/META-INF/aop.xml
@@ -4,6 +4,7 @@
<aspects>
<aspect name="kamon.play.instrumentation.RequestInstrumentation"/>
<aspect name="kamon.play.instrumentation.WSInstrumentation"/>
+ <aspect name="kamon.play.instrumentation.LoggerLikeInstrumentation"/>
</aspects>
<weaver>
diff --git a/kamon-play/src/main/resources/reference.conf b/kamon-play/src/main/resources/reference.conf
index 47a31ef4..72266a0c 100644
--- a/kamon-play/src/main/resources/reference.conf
+++ b/kamon-play/src/main/resources/reference.conf
@@ -3,6 +3,10 @@
# ================================== #
kamon {
+ metrics {
+ tick-interval = 1 hour
+ }
+
play {
include-trace-token-header = true
trace-token-header-name = "X-Trace-Token"
diff --git a/kamon-play/src/main/scala/kamon/play/Play.scala b/kamon-play/src/main/scala/kamon/play/Play.scala
index ca9c10e5..03436458 100644
--- a/kamon-play/src/main/scala/kamon/play/Play.scala
+++ b/kamon-play/src/main/scala/kamon/play/Play.scala
@@ -18,6 +18,8 @@ package kamon.play
import akka.actor.{ ExtendedActorSystem, Extension, ExtensionIdProvider, ExtensionId }
import kamon.Kamon
+import kamon.http.HttpServerMetrics
+import kamon.metric.Metrics
object Play extends ExtensionId[PlayExtension] with ExtensionIdProvider {
override def lookup(): ExtensionId[_ <: Extension] = Play
@@ -29,6 +31,7 @@ class PlayExtension(private val system: ExtendedActorSystem) extends Kamon.Exten
private val config = system.settings.config.getConfig("kamon.play")
+ val httpServerMetrics = Kamon(Metrics)(system).register(HttpServerMetrics, HttpServerMetrics.Factory).get
val defaultDispatcher = system.dispatchers.lookup(config.getString("dispatcher"))
val includeTraceToken: Boolean = config.getBoolean("include-trace-token-header")
val traceTokenHeaderName: String = config.getString("trace-token-header-name")
diff --git a/kamon-play/src/main/scala/kamon/play/instrumentation/LoggerLikeInstrumentation.scala b/kamon-play/src/main/scala/kamon/play/instrumentation/LoggerLikeInstrumentation.scala
new file mode 100644
index 00000000..b7afeb76
--- /dev/null
+++ b/kamon-play/src/main/scala/kamon/play/instrumentation/LoggerLikeInstrumentation.scala
@@ -0,0 +1,70 @@
+/* =========================================================================================
+ * Copyright © 2013-2014 the kamon project <http://kamon.io/>
+ *
+ * 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.play.instrumentation
+
+import kamon.trace.{ TraceContext, TraceContextAware }
+import org.aspectj.lang.ProceedingJoinPoint
+import org.aspectj.lang.annotation._
+import org.slf4j.MDC
+
+@Aspect
+class LoggerLikeInstrumentation {
+
+ import LoggerLikeInstrumentation._
+
+ @DeclareMixin("play.api.LoggerLike+")
+ def mixinContextAwareToLoggerLike: TraceContextAware = TraceContextAware.default
+
+ @Pointcut("execution(* play.api.LoggerLike+.info(..))")
+ def infoPointcut(): Unit = {}
+
+ @Pointcut("execution(* play.api.LoggerLike+.warn(..))")
+ def warnPointcut(): Unit = {}
+
+ @Pointcut("execution(* play.api.LoggerLike+.error(..))")
+ def errorPointcut(): Unit = {}
+
+ @Pointcut("execution(* play.api.LoggerLike+.trace(..))")
+ def tracePointcut(): Unit = {}
+
+ @Around("(infoPointcut() || warnPointcut() || errorPointcut() || tracePointcut()) && this(logger)")
+ def aroundLog(pjp: ProceedingJoinPoint, logger: TraceContextAware): Any = {
+ withMDC(logger.traceContext) {
+ pjp.proceed()
+ }
+ }
+}
+
+object LoggerLikeInstrumentation {
+ def withMDC[A](currentContext: Option[TraceContext])(block: ⇒ A): A = {
+ val keys = currentContext.map(extractProperties).map(putAndExtractKeys)
+
+ try block finally keys.map(k ⇒ k.foreach(MDC.remove(_)))
+ }
+
+ 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(ctx: TraceContext): Iterable[Map[String, Any]] = ctx.traceLocalStorage.underlyingStorage.values.map {
+ case traceLocalValue @ (p: Product) ⇒ {
+ val properties = p.productIterator
+ traceLocalValue.getClass.getDeclaredFields.filter(field ⇒ field.getName != "$outer").map(_.getName -> properties.next).toMap
+ }
+ case anything ⇒ Map.empty[String, Any]
+ }
+}
+
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 00170b1b..3247fcc9 100644
--- a/kamon-play/src/main/scala/kamon/play/instrumentation/RequestInstrumentation.scala
+++ b/kamon-play/src/main/scala/kamon/play/instrumentation/RequestInstrumentation.scala
@@ -1,23 +1,22 @@
-/* ===================================================
+/* =========================================================================================
* Copyright © 2013-2014 the kamon project <http://kamon.io/>
*
- * 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
+ * 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
+ * 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.
- * ========================================================== */
+ * 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.play.instrumentation
import kamon.Kamon
-import kamon.play.Play
+import kamon.play.{ PlayExtension, Play }
import kamon.trace.{ TraceContextAware, TraceRecorder }
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation._
@@ -49,15 +48,21 @@ class RequestInstrumentation {
}
@Around("execution(* play.api.GlobalSettings+.doFilter(*)) && args(next)")
- def afterDoFilter(pjp: ProceedingJoinPoint, next: EssentialAction): Any = {
+ def aroundDoFilter(pjp: ProceedingJoinPoint, next: EssentialAction): Any = {
val essentialAction = (requestHeader: RequestHeader) ⇒ {
val incomingContext = TraceRecorder.currentContext
- val executor = Kamon(Play)(Akka.system()).defaultDispatcher
+ val playExtension = Kamon(Play)(Akka.system())
+ val executor = playExtension.defaultDispatcher
next(requestHeader).map {
result ⇒
+ TraceRecorder.currentContext.map { ctx ⇒
+ recordHttpServerMetrics(result, ctx.name, playExtension)
+ }
+
TraceRecorder.finish()
+
incomingContext match {
case None ⇒ result
case Some(traceContext) ⇒
@@ -71,6 +76,9 @@ class RequestInstrumentation {
pjp.proceed(Array(EssentialAction(essentialAction)))
}
+ def recordHttpServerMetrics(result: SimpleResult, traceName: String, playExtension: PlayExtension): Unit =
+ playExtension.httpServerMetrics.recordResponse(traceName, result.header.status.toString, 1L)
+
@Around("execution(* play.api.GlobalSettings+.onError(..)) && args(request, ex)")
def aroundOnError(pjp: ProceedingJoinPoint, request: TraceContextAware, ex: Throwable): Any = request.traceContext match {
case None ⇒ pjp.proceed()
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 f9e01471..b9f09111 100644
--- a/kamon-play/src/main/scala/kamon/play/instrumentation/WSInstrumentation.scala
+++ b/kamon-play/src/main/scala/kamon/play/instrumentation/WSInstrumentation.scala
@@ -19,7 +19,7 @@ package kamon.play.instrumentation
import org.aspectj.lang.annotation.{ Around, Pointcut, Aspect }
import org.aspectj.lang.ProceedingJoinPoint
import kamon.trace.TraceRecorder
-import kamon.metrics.TraceMetrics.HttpClientRequest
+import kamon.metric.TraceMetrics.HttpClientRequest
import play.api.libs.ws.WS.WSRequest
import scala.concurrent.Future
import play.api.libs.ws.Response
@@ -36,7 +36,7 @@ class WSInstrumentation {
def aroundExecuteRequest(pjp: ProceedingJoinPoint, request: WSRequest): Any = {
import WSInstrumentation._
- val completionHandle = TraceRecorder.startSegment(HttpClientRequest(request.url, UserTime), basicRequestAttributes(request))
+ val completionHandle = TraceRecorder.startSegment(HttpClientRequest(request.url), basicRequestAttributes(request))
val response = pjp.proceed().asInstanceOf[Future[Response]]
@@ -50,7 +50,6 @@ class WSInstrumentation {
}
object WSInstrumentation {
- val UserTime = "UserTime"
def basicRequestAttributes(request: WSRequest): Map[String, String] = {
Map[String, String](