diff options
Diffstat (limited to 'kamon-play/src/main')
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]( |