diff options
author | Ivan Topolnjak <ivantopo@gmail.com> | 2015-02-14 13:50:36 +0100 |
---|---|---|
committer | Ivan Topolnjak <ivantopo@gmail.com> | 2015-02-14 13:50:36 +0100 |
commit | e931727454fbd97fb39d163255edbcdcd7bcdbc6 (patch) | |
tree | 98b4bdcaa1af1dbbf201036ce05021bc096db62f /kamon-play/src/main/scala/kamon | |
parent | 8af0dfb1e2c8892023dd1bc6fbae1dae2ffb16ba (diff) | |
parent | 66b35556aa1bf0975cefa35603660991cdfcc526 (diff) | |
download | Kamon-e931727454fbd97fb39d163255edbcdcd7bcdbc6.tar.gz Kamon-e931727454fbd97fb39d163255edbcdcd7bcdbc6.tar.bz2 Kamon-e931727454fbd97fb39d163255edbcdcd7bcdbc6.zip |
Merge branch 'single-kamon-instance-per-jvm' into release-legacy-akka-2.2
Conflicts:
kamon-akka-remote/src/test/scala/kamon/akka/instrumentation/RemotingInstrumentationSpec.scala
kamon-core/src/main/scala/kamon/instrumentation/akka/ActorCellInstrumentation.scala
kamon-core/src/main/scala/kamon/instrumentation/akka/AskPatternInstrumentation.scala
kamon-core/src/main/scala/kamon/metric/MetricsExtension.scala
kamon-core/src/main/scala/kamon/metric/Subscriptions.scala
kamon-core/src/main/scala/kamon/metric/instrument/Gauge.scala
kamon-core/src/main/scala/kamon/metric/instrument/MinMaxCounter.scala
kamon-core/src/test/scala/kamon/instrumentation/akka/ActorCellInstrumentationSpec.scala
kamon-core/src/test/scala/kamon/metric/ActorMetricsSpec.scala
kamon-core/src/test/scala/kamon/metric/RouterMetricsSpec.scala
kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala
kamon-core/src/test/scala/kamon/metric/TickMetricSnapshotBufferSpec.scala
kamon-core/src/test/scala/kamon/metric/TraceMetricsSpec.scala
kamon-core/src/test/scala/kamon/metric/UserMetricsSpec.scala
kamon-core/src/test/scala/kamon/trace/TraceContextManipulationSpec.scala
kamon-datadog/src/main/scala/kamon/datadog/Datadog.scala
kamon-newrelic/src/main/scala/kamon/newrelic/Agent.scala
kamon-newrelic/src/main/scala/kamon/newrelic/MetricReporter.scala
kamon-play/src/main/scala/kamon/play/Play.scala
kamon-play/src/main/scala/kamon/play/action/KamonTraceActions.scala
kamon-play/src/main/scala/kamon/play/instrumentation/RequestInstrumentation.scala
kamon-play/src/main/scala/kamon/play/instrumentation/WSInstrumentation.scala
kamon-play/src/test/scala/kamon/play/RequestInstrumentationSpec.scala
kamon-statsd/src/main/scala/kamon/statsd/StatsD.scala
project/Dependencies.scala
version.sbt
Diffstat (limited to 'kamon-play/src/main/scala/kamon')
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 |