From bbf7afd85809f6d43b310290b4bb9102dd36043c Mon Sep 17 00:00:00 2001 From: Ivan Topolnak Date: Wed, 18 Sep 2013 18:43:11 -0300 Subject: basic newrelic reporting --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'project') diff --git a/project/Build.scala b/project/Build.scala index 0141540b..7fb935a2 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -19,7 +19,7 @@ object Build extends Build { .settings( libraryDependencies ++= - compile(akkaActor, akkaAgent, sprayCan, sprayClient, sprayRouting, sprayServlet, aspectJ, aspectJWeaver, metrics, sprayJson) ++ + compile(akkaActor, akkaAgent, sprayCan, sprayClient, sprayRouting, sprayServlet, aspectJ, aspectJWeaver, metrics, sprayJson, newrelic) ++ test(scalatest, akkaTestKit, sprayTestkit)) //.dependsOn(kamonDashboard) -- cgit v1.2.3 From 50823a3cc4f6644d569255c7e04423c36eedf295 Mon Sep 17 00:00:00 2001 From: Ivan Topolnak Date: Sat, 21 Sep 2013 22:57:43 -0300 Subject: wip --- kamon-core/src/main/resources/META-INF/aop.xml | 1 + .../src/main/scala/kamon/TraceContextSwap.scala | 4 +- .../ActorRefTellInstrumentation.scala | 4 +- .../SprayServerInstrumentation.scala | 57 ++++++++++++++++++++-- .../scala/kamon/newrelic/NewRelicReporting.scala | 23 ++++++++- .../src/main/scala/kamon/trace/UowTracing.scala | 1 + kamon-core/src/main/scala/test/PingPong.scala | 16 +++++- project/Dependencies.scala | 2 +- 8 files changed, 95 insertions(+), 13 deletions(-) (limited to 'project') diff --git a/kamon-core/src/main/resources/META-INF/aop.xml b/kamon-core/src/main/resources/META-INF/aop.xml index 10110300..4705c1f2 100644 --- a/kamon-core/src/main/resources/META-INF/aop.xml +++ b/kamon-core/src/main/resources/META-INF/aop.xml @@ -20,6 +20,7 @@ --> + diff --git a/kamon-core/src/main/scala/kamon/TraceContextSwap.scala b/kamon-core/src/main/scala/kamon/TraceContextSwap.scala index 4b5b66a9..c25e63d1 100644 --- a/kamon-core/src/main/scala/kamon/TraceContextSwap.scala +++ b/kamon-core/src/main/scala/kamon/TraceContextSwap.scala @@ -12,11 +12,11 @@ trait TraceContextSwap { def withContext[A](ctx: Option[TraceContext], primary: => A, fallback: => A): A = { ctx match { case Some(context) => { - MDC.put("uow", context.userContext.get.asInstanceOf[String]) + //MDC.put("uow", context.userContext.get.asInstanceOf[String]) Tracer.set(context) val bodyResult = primary Tracer.clear - MDC.remove("uow") + //MDC.remove("uow") bodyResult } diff --git a/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala b/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala index 4f0b8a08..fdd7b696 100644 --- a/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala +++ b/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala @@ -24,7 +24,7 @@ class ActorRefTellInstrumentation { @Around("sendingMessageToActorRef(actor, message, sender)") def around(pjp: ProceedingJoinPoint, actor: ActorRef, message: Any, sender: ActorRef): Unit = { import kamon.Instrument.instrumentation.sendMessageTransformation - + //println(s"====> [$sender] => [$actor] --- $message") pjp.proceedWithTarget(actor, sendMessageTransformation(sender, actor, message).asInstanceOf[AnyRef], sender) } } @@ -76,7 +76,7 @@ class UnregisteredActorRefInstrumentation { @Around("sprayResponderHandle(message, sender)") def sprayInvokeAround(pjp: ProceedingJoinPoint, message: Any, sender: ActorRef): Unit = { import ProceedingJoinPointPimp._ - println("Handling unregistered actor ref message: "+message) + //println("Handling unregistered actor ref message: "+message) message match { case SimpleTraceMessage(msg, ctx) => { ctx match { diff --git a/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala b/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala index 6573549d..f8ab709e 100644 --- a/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala +++ b/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala @@ -1,10 +1,12 @@ package kamon.instrumentation -import org.aspectj.lang.annotation.{After, Pointcut, Aspect} -import kamon.Tracer -import kamon.trace.UowTracing.{Finish, Rename} +import org.aspectj.lang.annotation.{DeclareMixin, After, Pointcut, Aspect} +import kamon.{TraceContext, Tracer} +import kamon.trace.UowTracing.{WebExternal, Finish, Rename} import spray.http.HttpRequest import spray.can.server.OpenRequestComponent +import spray.can.client.HttpHostConnector.RequestContext +import spray.http.HttpHeaders.Host @Aspect class SprayServerInstrumentation { @@ -17,7 +19,7 @@ class SprayServerInstrumentation { //@After("openRequestInit()") //def afterInit(): Unit = { Tracer.start - println("Created the context: " + Tracer.context() + " for the transaction: " + request.uri.path.toString()) + //println("Created the context: " + Tracer.context() + " for the transaction: " + request.uri.path.toString()) Tracer.context().map(_.entries ! Rename(request.uri.path.toString())) } @@ -30,4 +32,51 @@ class SprayServerInstrumentation { Tracer.context().map(_.entries ! Finish()) } + + + + + @Pointcut("execution(spray.can.client.HttpHostConnector.RequestContext.new(..)) && this(ctx)") + def requestRecordInit(ctx: TracingAwareRequestContext): Unit = {} + + @After("requestRecordInit(ctx)") + def whenCreatedRequestRecord(ctx: TracingAwareRequestContext): Unit = { + // Necessary to force the initialization of TracingAwareRequestContext at the moment of creation. + ctx.context + } + + + + + + + @Pointcut("execution(* spray.can.client.HttpHostConnectionSlot.dispatchToCommander(..)) && args(ctx, msg)") + def requestRecordInit2(ctx: TracingAwareRequestContext, msg: Any): Unit = {} + + @After("requestRecordInit2(ctx, msg)") + def whenCreatedRequestRecord2(ctx: TracingAwareRequestContext, msg: Any): Unit = { + println("=======> Spent in WEB External: " + (System.nanoTime() - ctx.timestamp)) + + // TODO: REMOVE THIS: + val request = (ctx.asInstanceOf[RequestContext]).request + + ctx.context.map(_.entries ! WebExternal(ctx.timestamp, System.nanoTime(), request.header[Host].map(_.host).getOrElse("UNKNOWN"))) + + } +} + +trait TracingAwareRequestContext { + def context: Option[TraceContext] + def timestamp: Long +} + +case class DefaultTracingAwareRequestContext(context: Option[TraceContext] = Tracer.context(), + timestamp: Long = System.nanoTime) extends TracingAwareRequestContext + + +@Aspect +class SprayRequestContextTracing { + + @DeclareMixin("spray.can.client.HttpHostConnector.RequestContext") + def mixin: TracingAwareRequestContext = DefaultTracingAwareRequestContext() } \ No newline at end of file diff --git a/kamon-core/src/main/scala/kamon/newrelic/NewRelicReporting.scala b/kamon-core/src/main/scala/kamon/newrelic/NewRelicReporting.scala index 131ecba9..33f169da 100644 --- a/kamon-core/src/main/scala/kamon/newrelic/NewRelicReporting.scala +++ b/kamon-core/src/main/scala/kamon/newrelic/NewRelicReporting.scala @@ -3,6 +3,7 @@ package kamon.newrelic import akka.actor.Actor import kamon.trace.UowTrace import com.newrelic.api.agent.{Trace, NewRelic} +import kamon.trace.UowTracing.WebExternal class NewRelicReporting extends Actor { @@ -12,10 +13,28 @@ class NewRelicReporting extends Actor { //@Trace def recordTransaction(uowTrace: UowTrace): Unit = { - val time = (uowTrace.segments.last.timestamp - uowTrace.segments.head.timestamp)/1E9 + val time = ((uowTrace.segments.last.timestamp - uowTrace.segments.head.timestamp)/1E9) - NewRelic.recordMetric("WebTransaction/Custom" + uowTrace.name, time.toFloat) + NewRelic.recordMetric("WebTransaction/Custom" + uowTrace.name, time.toFloat ) NewRelic.recordMetric("WebTransaction", time.toFloat) NewRelic.recordMetric("HttpDispatcher", time.toFloat) + + uowTrace.segments.collect { case we: WebExternal => we }.foreach { webExternalTrace => + val external = ((webExternalTrace.end - webExternalTrace.start)/1E9).toFloat + NewRelic.recordMetric(s"External/all", external) + NewRelic.recordMetric(s"External/allWeb", external) + + NewRelic.recordMetric(s"Solr/all", 0.1F) + NewRelic.recordMetric(s"Solr/allWeb", 0.1F) + NewRelic.recordMetric(s"Solr/set", 0.1F) + NewRelic.recordMetric(s"Solr/set/WebTransaction/Custom/test", 0.1F) + + NewRelic.recordMetric(s"External/${webExternalTrace.host}/http", external) + NewRelic.recordMetric(s"External/${webExternalTrace.host}/all", external) + NewRelic.recordMetric(s"External/${webExternalTrace.host}/http/" + "WebTransaction/Custom" + uowTrace.name, external) + + + } + } } diff --git a/kamon-core/src/main/scala/kamon/trace/UowTracing.scala b/kamon-core/src/main/scala/kamon/trace/UowTracing.scala index b38d3d95..8efc46b2 100644 --- a/kamon-core/src/main/scala/kamon/trace/UowTracing.scala +++ b/kamon-core/src/main/scala/kamon/trace/UowTracing.scala @@ -16,6 +16,7 @@ object UowTracing { case class Start() extends AutoTimestamp case class Finish() extends AutoTimestamp case class Rename(name: String) extends AutoTimestamp + case class WebExternal(start: Long, end: Long, host: String) extends AutoTimestamp } case class UowTrace(name: String, segments: Seq[UowSegment]) diff --git a/kamon-core/src/main/scala/test/PingPong.scala b/kamon-core/src/main/scala/test/PingPong.scala index b78f1d79..93aa322d 100644 --- a/kamon-core/src/main/scala/test/PingPong.scala +++ b/kamon-core/src/main/scala/test/PingPong.scala @@ -4,6 +4,8 @@ import akka.actor.{Deploy, Props, Actor, ActorSystem} import java.util.concurrent.atomic.AtomicLong import kamon.Tracer import spray.routing.SimpleRoutingApp +import akka.util.Timeout +import spray.httpx.RequestBuilding object PingPong extends App { import scala.concurrent.duration._ @@ -49,13 +51,23 @@ class Ponger extends Actor { } -object SimpleRequestProcessor extends App with SimpleRoutingApp { +object SimpleRequestProcessor extends App with SimpleRoutingApp with RequestBuilding { + import scala.concurrent.duration._ + import spray.client.pipelining._ + implicit val system = ActorSystem("test") + import system.dispatcher + + implicit val timeout = Timeout(30 seconds) + + val pipeline = sendReceive startServer(interface = "localhost", port = 9090) { get { path("test"){ - complete("OK") + complete { + pipeline(Get("http://www.despegar.com.ar")).map(r => "Ok") + } } } } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index b61f0979..fb1b43d5 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -7,7 +7,7 @@ object Dependencies { "spray nightlies repo" at "http://nightlies.spray.io" ) - val sprayVersion = "1.2-20130801" + val sprayVersion = "1.2-SNAPSHOT" val akkaVersion = "2.2.0" val sprayCan = "io.spray" % "spray-can" % sprayVersion -- cgit v1.2.3 From 604d5801332838f8bea25fe25cb8df5dbb82af08 Mon Sep 17 00:00:00 2001 From: Ivan Topolnak Date: Wed, 25 Sep 2013 19:45:54 -0300 Subject: wip --- kamon-core/src/main/resources/META-INF/aop.xml | 1 + kamon-core/src/main/resources/application.conf | 1 + kamon-core/src/main/scala/kamon/TraceContext.scala | 2 +- .../ActorRefTellInstrumentation.scala | 4 ++- .../SprayServerInstrumentation.scala | 33 ++++++++++++++++----- .../scala/kamon/newrelic/NewRelicReporting.scala | 5 ---- .../src/main/scala/kamon/trace/UowTracing.scala | 12 ++++++-- kamon-core/src/test/scala/ExtraSpec.scala | 34 ++++++++++++++++++++++ project/NewRelic.scala | 2 +- project/Settings.scala | 2 +- 10 files changed, 77 insertions(+), 19 deletions(-) create mode 100644 kamon-core/src/test/scala/ExtraSpec.scala (limited to 'project') diff --git a/kamon-core/src/main/resources/META-INF/aop.xml b/kamon-core/src/main/resources/META-INF/aop.xml index 4705c1f2..79692dd3 100644 --- a/kamon-core/src/main/resources/META-INF/aop.xml +++ b/kamon-core/src/main/resources/META-INF/aop.xml @@ -21,6 +21,7 @@ + diff --git a/kamon-core/src/main/resources/application.conf b/kamon-core/src/main/resources/application.conf index 647939f8..d51f6b15 100644 --- a/kamon-core/src/main/resources/application.conf +++ b/kamon-core/src/main/resources/application.conf @@ -1,6 +1,7 @@ akka { loglevel = DEBUG stdout-loglevel = DEBUG + log-dead-letters = on #extensions = ["kamon.dashboard.DashboardExtension"] diff --git a/kamon-core/src/main/scala/kamon/TraceContext.scala b/kamon-core/src/main/scala/kamon/TraceContext.scala index a1476ae0..73186a18 100644 --- a/kamon-core/src/main/scala/kamon/TraceContext.scala +++ b/kamon-core/src/main/scala/kamon/TraceContext.scala @@ -19,7 +19,7 @@ case class TraceContext(id: Long, entries: ActorRef, userContext: Option[Any] = object TraceContext { val reporter = Kamon.actorSystem.actorOf(Props[NewRelicReporting]) val traceIdCounter = new AtomicLong - def apply()(implicit system: ActorSystem) = new TraceContext(100, system.actorOf(UowTraceAggregator.props(reporter, 30 seconds))) // TODO: Move to a kamon specific supervisor, like /user/kamon/tracer + def apply()(implicit system: ActorSystem) = new TraceContext(100, system.actorOf(UowTraceAggregator.props(reporter, 30 seconds), "tracer-"+traceIdCounter.incrementAndGet())) // TODO: Move to a kamon specific supervisor, like /user/kamon/tracer } diff --git a/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala b/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala index fdd7b696..d92d7f6c 100644 --- a/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala +++ b/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala @@ -34,6 +34,7 @@ class ActorRefTellInstrumentation { @Aspect("""perthis(actorCellCreation(akka.actor.ActorSystem, akka.actor.ActorRef, akka.actor.Props, akka.dispatch.MessageDispatcher, akka.actor.ActorRef))""") class ActorCellInvokeInstrumentation { var instrumentation = ActorReceiveInvokeInstrumentation.noopPreReceive + var self: ActorRef = _ // AKKA 2.2 introduces the dispatcher parameter. Maybe we could provide a dual pointcut. @Pointcut("execution(akka.actor.ActorCell.new(..)) && args(system, ref, props, dispatcher, parent)") @@ -42,6 +43,7 @@ class ActorCellInvokeInstrumentation { @After("actorCellCreation(system, ref, props, dispatcher, parent)") def registerMetricsInRegistry(system: ActorSystem, ref: ActorRef, props: Props, dispatcher: MessageDispatcher, parent: ActorRef): Unit = { instrumentation = kamon.Instrument.instrumentation.receiveInvokeInstrumentation(system, ref, props, dispatcher, parent) + self = ref } @@ -53,7 +55,7 @@ class ActorCellInvokeInstrumentation { import ProceedingJoinPointPimp._ val (originalEnvelope, ctx) = instrumentation.preReceive(envelope) - //println("Test") + //println(s"====>[$ctx] ## [${originalEnvelope.sender}] => [$self] --- ${originalEnvelope.message}") ctx match { case Some(c) => { //MDC.put("uow", c.userContext.get.asInstanceOf[String]) diff --git a/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala b/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala index f8ab709e..9422a9f7 100644 --- a/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala +++ b/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala @@ -4,26 +4,39 @@ import org.aspectj.lang.annotation.{DeclareMixin, After, Pointcut, Aspect} import kamon.{TraceContext, Tracer} import kamon.trace.UowTracing.{WebExternal, Finish, Rename} import spray.http.HttpRequest -import spray.can.server.OpenRequestComponent +import spray.can.server.{OpenRequest, OpenRequestComponent} import spray.can.client.HttpHostConnector.RequestContext import spray.http.HttpHeaders.Host +trait ContextAware { + def traceContext: Option[TraceContext] +} + +@Aspect +class SprayOpenRequestContextTracing { + @DeclareMixin("spray.can.server.OpenRequestComponent.DefaultOpenRequest") + def mixinContextAwareToOpenRequest: ContextAware = new ContextAware { + val traceContext: Option[TraceContext] = Tracer.context() + } +} + @Aspect class SprayServerInstrumentation { - @Pointcut("execution(spray.can.server.OpenRequestComponent$DefaultOpenRequest.new(..)) && args(enclosing, request, closeAfterResponseCompletion, timestamp)") - def openRequestInit(enclosing: OpenRequestComponent, request: HttpRequest, closeAfterResponseCompletion: Boolean, timestamp: Long): Unit = {} + @Pointcut("execution(spray.can.server.OpenRequestComponent$DefaultOpenRequest.new(..)) && this(openRequest) && args(enclosing, request, closeAfterResponseCompletion, timestamp)") + def openRequestInit(openRequest: OpenRequest, enclosing: OpenRequestComponent, request: HttpRequest, closeAfterResponseCompletion: Boolean, timestamp: Long): Unit = {} - @After("openRequestInit(enclosing, request, closeAfterResponseCompletion, timestamp)") - def afterInit(enclosing: OpenRequestComponent, request: HttpRequest, closeAfterResponseCompletion: Boolean, timestamp: Long): Unit = { + @After("openRequestInit(openRequest, enclosing, request, closeAfterResponseCompletion, timestamp)") + def afterInit(openRequest: OpenRequest, enclosing: OpenRequestComponent, request: HttpRequest, closeAfterResponseCompletion: Boolean, timestamp: Long): Unit = { //@After("openRequestInit()") //def afterInit(): Unit = { Tracer.start - //println("Created the context: " + Tracer.context() + " for the transaction: " + request.uri.path.toString()) + //openRequest.traceContext + println("Created the context: " + Tracer.context() + " for the transaction: " + request) Tracer.context().map(_.entries ! Rename(request.uri.path.toString())) } - @Pointcut("execution(* spray.can.server.OpenRequest.handleResponseEndAndReturnNextOpenRequest(..))") + @Pointcut("execution(* spray.can.server.OpenRequestComponent$DefaultOpenRequest.handleResponseEndAndReturnNextOpenRequest(..))") def openRequestCreation(): Unit = {} @After("openRequestCreation()") @@ -31,6 +44,10 @@ class SprayServerInstrumentation { println("Finishing a request: " + Tracer.context()) Tracer.context().map(_.entries ! Finish()) + + if(Tracer.context().isEmpty) { + println("WOOOOOPAAAAAAAAA") + } } @@ -55,7 +72,7 @@ class SprayServerInstrumentation { @After("requestRecordInit2(ctx, msg)") def whenCreatedRequestRecord2(ctx: TracingAwareRequestContext, msg: Any): Unit = { - println("=======> Spent in WEB External: " + (System.nanoTime() - ctx.timestamp)) + //println("=======> Spent in WEB External: " + (System.nanoTime() - ctx.timestamp)) // TODO: REMOVE THIS: val request = (ctx.asInstanceOf[RequestContext]).request diff --git a/kamon-core/src/main/scala/kamon/newrelic/NewRelicReporting.scala b/kamon-core/src/main/scala/kamon/newrelic/NewRelicReporting.scala index 33f169da..31e50cfe 100644 --- a/kamon-core/src/main/scala/kamon/newrelic/NewRelicReporting.scala +++ b/kamon-core/src/main/scala/kamon/newrelic/NewRelicReporting.scala @@ -24,11 +24,6 @@ class NewRelicReporting extends Actor { NewRelic.recordMetric(s"External/all", external) NewRelic.recordMetric(s"External/allWeb", external) - NewRelic.recordMetric(s"Solr/all", 0.1F) - NewRelic.recordMetric(s"Solr/allWeb", 0.1F) - NewRelic.recordMetric(s"Solr/set", 0.1F) - NewRelic.recordMetric(s"Solr/set/WebTransaction/Custom/test", 0.1F) - NewRelic.recordMetric(s"External/${webExternalTrace.host}/http", external) NewRelic.recordMetric(s"External/${webExternalTrace.host}/all", external) NewRelic.recordMetric(s"External/${webExternalTrace.host}/http/" + "WebTransaction/Custom" + uowTrace.name, external) diff --git a/kamon-core/src/main/scala/kamon/trace/UowTracing.scala b/kamon-core/src/main/scala/kamon/trace/UowTracing.scala index 8efc46b2..48def942 100644 --- a/kamon-core/src/main/scala/kamon/trace/UowTracing.scala +++ b/kamon-core/src/main/scala/kamon/trace/UowTracing.scala @@ -1,8 +1,13 @@ package kamon.trace -import akka.actor.{Props, ActorRef, Actor} +import akka.actor._ import kamon.trace.UowTracing.{Start, Finish, Rename} import scala.concurrent.duration.Duration +import kamon.trace.UowTracing.Finish +import kamon.trace.UowTracing.Rename +import kamon.trace.UowTrace +import kamon.trace.UowTracing.Start +import scala.Some sealed trait UowSegment { def timestamp: Long @@ -22,7 +27,7 @@ object UowTracing { case class UowTrace(name: String, segments: Seq[UowSegment]) -class UowTraceAggregator(reporting: ActorRef, aggregationTimeout: Duration) extends Actor { +class UowTraceAggregator(reporting: ActorRef, aggregationTimeout: Duration) extends Actor with ActorLogging { context.setReceiveTimeout(aggregationTimeout) self ! Start() @@ -33,6 +38,9 @@ class UowTraceAggregator(reporting: ActorRef, aggregationTimeout: Duration) exte case finish: Finish => segments = segments :+ finish; finishTracing() case Rename(newName) => name = Some(newName) case segment: UowSegment => segments = segments :+ segment + case ReceiveTimeout => + log.warning("Transaction {} did not complete properly, the recorded segments are: {}", name, segments) + context.stop(self) } def finishTracing(): Unit = { diff --git a/kamon-core/src/test/scala/ExtraSpec.scala b/kamon-core/src/test/scala/ExtraSpec.scala new file mode 100644 index 00000000..b8dc053d --- /dev/null +++ b/kamon-core/src/test/scala/ExtraSpec.scala @@ -0,0 +1,34 @@ +import akka.actor.ActorSystem +import akka.testkit.TestKit +import org.scalatest.WordSpecLike +import shapeless._ + +class ExtraSpec extends TestKit(ActorSystem("ExtraSpec")) with WordSpecLike { + + "the Extra pattern helper" should { + "be constructed from a finite number of types" in { + Extra.expecting[String :: Int :: HNil].as[Person] + } + } + + case class Person(name: String, age: Int) +} + +/** + * Desired Features: + * 1. Expect messages of different types, apply a function and forward to some other. + */ + +object Extra { + def expecting[T <: HList] = new Object { + def as[U <: Product] = ??? + } +} + +/* +extra of { + expect[A] in { actor ! msg} + expect[A] in { actor ! msg} +} as (A, A) pipeTo (z)*/ + + diff --git a/project/NewRelic.scala b/project/NewRelic.scala index 74f8fc30..e662d4e3 100644 --- a/project/NewRelic.scala +++ b/project/NewRelic.scala @@ -8,6 +8,6 @@ object NewRelic { lazy val newrelicSettings = SbtNewrelic.newrelicSettings ++ Seq( javaOptions in run <++= jvmOptions in newrelic, - newrelicVersion in newrelic := "2.19.0" + newrelicVersion in newrelic := "2.20.0" ) } diff --git a/project/Settings.scala b/project/Settings.scala index df120b0a..0089c04d 100644 --- a/project/Settings.scala +++ b/project/Settings.scala @@ -3,7 +3,7 @@ import Keys._ import spray.revolver.RevolverPlugin.Revolver object Settings { - val VERSION = "0.1-SNAPSHOT" + val VERSION = "0.2-SNAPSHOT" lazy val basicSettings = seq( version := VERSION, -- cgit v1.2.3 From d7f1e195da11f977d5fdbf74598e499156de8dc4 Mon Sep 17 00:00:00 2001 From: Ivan Topolnak Date: Mon, 30 Sep 2013 11:21:25 -0300 Subject: wip --- kamon-core/src/main/resources/application.conf | 6 ++--- .../ActorRefTellInstrumentation.scala | 14 +++++++--- .../instrumentation/RunnableInstrumentation.scala | 12 ++++++--- .../SprayServerInstrumentation.scala | 8 +++--- kamon-core/src/main/scala/test/PingPong.scala | 31 +++++++++++++++++++++- .../instrumentation/ActorInstrumentationSpec.scala | 22 ++++++++++++++- project/Build.scala | 4 +++ project/Settings.scala | 2 +- 8 files changed, 83 insertions(+), 16 deletions(-) (limited to 'project') diff --git a/kamon-core/src/main/resources/application.conf b/kamon-core/src/main/resources/application.conf index d51f6b15..c87c0ced 100644 --- a/kamon-core/src/main/resources/application.conf +++ b/kamon-core/src/main/resources/application.conf @@ -1,7 +1,7 @@ akka { - loglevel = DEBUG - stdout-loglevel = DEBUG - log-dead-letters = on + loglevel = INFO + stdout-loglevel = INFO + log-dead-letters = off #extensions = ["kamon.dashboard.DashboardExtension"] diff --git a/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala b/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala index d92d7f6c..df124f41 100644 --- a/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala +++ b/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala @@ -64,8 +64,11 @@ class ActorCellInvokeInstrumentation { Tracer.clear //MDC.remove("uow") } - case None => pjp.proceedWith(originalEnvelope) + case None => + assert(Tracer.context() == None) + pjp.proceedWith(originalEnvelope) } + Tracer.clear } } @@ -79,6 +82,7 @@ class UnregisteredActorRefInstrumentation { def sprayInvokeAround(pjp: ProceedingJoinPoint, message: Any, sender: ActorRef): Unit = { import ProceedingJoinPointPimp._ //println("Handling unregistered actor ref message: "+message) + message match { case SimpleTraceMessage(msg, ctx) => { ctx match { @@ -87,10 +91,14 @@ class UnregisteredActorRefInstrumentation { pjp.proceedWith(msg.asInstanceOf[AnyRef]) // TODO: define if we should use Any or AnyRef and unify with the rest of the instrumentation. Tracer.clear } - case None => pjp.proceedWith(msg.asInstanceOf[AnyRef]) + case None => + assert(Tracer.context() == None) + pjp.proceedWith(msg.asInstanceOf[AnyRef]) } } - case _ => pjp.proceed + case _ => + //assert(Tracer.context() == None) + pjp.proceed } } } diff --git a/kamon-core/src/main/scala/kamon/instrumentation/RunnableInstrumentation.scala b/kamon-core/src/main/scala/kamon/instrumentation/RunnableInstrumentation.scala index 30041321..393293f1 100644 --- a/kamon-core/src/main/scala/kamon/instrumentation/RunnableInstrumentation.scala +++ b/kamon-core/src/main/scala/kamon/instrumentation/RunnableInstrumentation.scala @@ -37,7 +37,7 @@ class RunnableInstrumentation { * Aspect members */ - private val traceContext = Tracer.context + private var traceContext = Tracer.context /** @@ -47,15 +47,21 @@ class RunnableInstrumentation { @Before("instrumentedRunnableCreation()") def beforeCreation = { - //println((new Throwable).getStackTraceString) + traceContext = Tracer.context + /* if(traceContext.isEmpty) + println("NO TRACE CONTEXT FOR RUNNABLE at: [[[%s]]]", (new Throwable).getStackTraceString)//println((new Throwable).getStackTraceString) + else + println("SUPER TRACE CONTEXT FOR RUNNABLE at: [[[%s]]]", (new Throwable).getStackTraceString)*/ } @Around("runnableExecution()") def around(pjp: ProceedingJoinPoint) = { import pjp._ - + /*if(traceContext.isEmpty) + println("OOHHH NOOOOO")*/ withContext(traceContext, proceed()) } } + diff --git a/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala b/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala index 9422a9f7..4eafcebe 100644 --- a/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala +++ b/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala @@ -32,7 +32,7 @@ class SprayServerInstrumentation { //def afterInit(): Unit = { Tracer.start //openRequest.traceContext - println("Created the context: " + Tracer.context() + " for the transaction: " + request) + //println("Created the context: " + Tracer.context() + " for the transaction: " + request) Tracer.context().map(_.entries ! Rename(request.uri.path.toString())) } @@ -41,13 +41,13 @@ class SprayServerInstrumentation { @After("openRequestCreation()") def afterFinishingRequest(): Unit = { - println("Finishing a request: " + Tracer.context()) + //println("Finishing a request: " + Tracer.context()) Tracer.context().map(_.entries ! Finish()) - +/* if(Tracer.context().isEmpty) { println("WOOOOOPAAAAAAAAA") - } + }*/ } diff --git a/kamon-core/src/main/scala/test/PingPong.scala b/kamon-core/src/main/scala/test/PingPong.scala index 93aa322d..c942c6ab 100644 --- a/kamon-core/src/main/scala/test/PingPong.scala +++ b/kamon-core/src/main/scala/test/PingPong.scala @@ -1,11 +1,12 @@ package test -import akka.actor.{Deploy, Props, Actor, ActorSystem} +import akka.actor._ import java.util.concurrent.atomic.AtomicLong import kamon.Tracer import spray.routing.SimpleRoutingApp import akka.util.Timeout import spray.httpx.RequestBuilding +import scala.concurrent.Future object PingPong extends App { import scala.concurrent.duration._ @@ -54,6 +55,7 @@ class Ponger extends Actor { object SimpleRequestProcessor extends App with SimpleRoutingApp with RequestBuilding { import scala.concurrent.duration._ import spray.client.pipelining._ + import akka.pattern.ask implicit val system = ActorSystem("test") import system.dispatcher @@ -61,6 +63,7 @@ object SimpleRequestProcessor extends App with SimpleRoutingApp with RequestBuil implicit val timeout = Timeout(30 seconds) val pipeline = sendReceive + val replier = system.actorOf(Props[Replier]) startServer(interface = "localhost", port = 9090) { get { @@ -68,8 +71,34 @@ object SimpleRequestProcessor extends App with SimpleRoutingApp with RequestBuil complete { pipeline(Get("http://www.despegar.com.ar")).map(r => "Ok") } + } ~ + path("reply") { + complete { + if (Tracer.context().isEmpty) + println("ROUTE NO CONTEXT") + + (replier ? "replytome").mapTo[String] + } + } ~ + path("ok") { + complete("ok") + } ~ + path("future") { + dynamic { + complete(Future { "OK" }) + } } } } } + +class Replier extends Actor with ActorLogging { + def receive = { + case _ => + if(Tracer.context.isEmpty) + log.warning("PROCESSING A MESSAGE WITHOUT CONTEXT") + + sender ! "Ok" + } +} diff --git a/kamon-core/src/test/scala/akka/instrumentation/ActorInstrumentationSpec.scala b/kamon-core/src/test/scala/akka/instrumentation/ActorInstrumentationSpec.scala index ccc7740b..454b4514 100644 --- a/kamon-core/src/test/scala/akka/instrumentation/ActorInstrumentationSpec.scala +++ b/kamon-core/src/test/scala/akka/instrumentation/ActorInstrumentationSpec.scala @@ -8,6 +8,7 @@ import kamon.{TraceContext, Tracer} import akka.pattern.{pipe, ask} import akka.util.Timeout import scala.concurrent.duration._ +import scala.concurrent.{Await, Future} import akka.routing.RoundRobinRouter @@ -35,11 +36,30 @@ class ActorInstrumentationSpec extends TestKit(ActorSystem("ActorInstrumentation expectMsg(Some(testTraceContext)) } - "propagate the trace context to actors behind a rounter" in new RoutedTraceContextEchoFixture { + "propagate the trace context to actors behind a router" in new RoutedTraceContextEchoFixture { val contexts: Seq[Option[TraceContext]] = for(_ <- 1 to 10) yield Some(tellWithNewContext(echo, "test")) expectMsgAllOf(contexts: _*) } + + "propagate with many asks" in { + val echo = system.actorOf(Props[TraceContextEcho]) + val iterations = 50000 + implicit val timeout = Timeout(10 seconds) + + val futures = for(_ <- 1 to iterations) yield { + Tracer.start + val result = (echo ? "test") + Tracer.clear + + result + } + + val allResults = Await.result(Future.sequence(futures), 10 seconds) + assert(iterations == allResults.collect { + case Some(_) => 1 + }.sum) + } } } diff --git a/project/Build.scala b/project/Build.scala index 7fb935a2..f8f242a4 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -10,6 +10,10 @@ object Build extends Build { lazy val root = Project("root", file(".")) .aggregate(kamonCore, kamonUow, kamonDashboard) .settings(basicSettings: _*) + .settings( + publish := (), + publishLocal := () + ) lazy val kamonCore = Project("kamon-core", file("kamon-core")) .settings(basicSettings: _*) diff --git a/project/Settings.scala b/project/Settings.scala index 0089c04d..6389bcf4 100644 --- a/project/Settings.scala +++ b/project/Settings.scala @@ -3,7 +3,7 @@ import Keys._ import spray.revolver.RevolverPlugin.Revolver object Settings { - val VERSION = "0.2-SNAPSHOT" + val VERSION = "0.0.1" lazy val basicSettings = seq( version := VERSION, -- cgit v1.2.3 From fdd4b8817a57c94f7228c5ad6a8028f5aa7a21e4 Mon Sep 17 00:00:00 2001 From: Ivan Topolnak Date: Mon, 30 Sep 2013 16:36:44 -0300 Subject: remove spray-servlet dependency --- project/Build.scala | 2 +- project/Dependencies.scala | 1 - project/Settings.scala | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'project') diff --git a/project/Build.scala b/project/Build.scala index f8f242a4..80bc5d32 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -23,7 +23,7 @@ object Build extends Build { .settings( libraryDependencies ++= - compile(akkaActor, akkaAgent, sprayCan, sprayClient, sprayRouting, sprayServlet, aspectJ, aspectJWeaver, metrics, sprayJson, newrelic) ++ + compile(akkaActor, akkaAgent, sprayCan, sprayClient, sprayRouting, aspectJ, aspectJWeaver, metrics, sprayJson, newrelic) ++ test(scalatest, akkaTestKit, sprayTestkit)) //.dependsOn(kamonDashboard) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index fb1b43d5..7540ef08 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -14,7 +14,6 @@ object Dependencies { val sprayRouting = "io.spray" % "spray-routing" % sprayVersion val sprayTestkit = "io.spray" % "spray-testkit" % sprayVersion val sprayClient = "io.spray" % "spray-client" % sprayVersion - val sprayServlet = "io.spray" % "spray-servlet" % sprayVersion val sprayJson = "io.spray" %% "spray-json" % "1.2.3" val scalaReflect = "org.scala-lang" % "scala-reflect" % "2.10.1" val akkaActor = "com.typesafe.akka" %% "akka-actor" % akkaVersion diff --git a/project/Settings.scala b/project/Settings.scala index 6389bcf4..6aca813f 100644 --- a/project/Settings.scala +++ b/project/Settings.scala @@ -3,7 +3,7 @@ import Keys._ import spray.revolver.RevolverPlugin.Revolver object Settings { - val VERSION = "0.0.1" + val VERSION = "0.0.2" lazy val basicSettings = seq( version := VERSION, @@ -23,7 +23,7 @@ object Settings { "-language:implicitConversions", "-Xlog-reflective-calls" ), - publishTo := Some("Nexus" at "http://nexus.despegar.it:8080/nexus/content/repositories/snapshots") + publishTo := Some("Nexus" at "http://nexus.despegar.it:8080/nexus/content/repositories/releases") ) -- cgit v1.2.3 From 5ffb3a50be348237fda9a8176b508284c59261af Mon Sep 17 00:00:00 2001 From: Diego Parra Date: Thu, 19 Sep 2013 08:58:34 -0300 Subject: Envelope Instrumentation and some cleanup --- kamon-core/src/main/resources/META-INF/aop.xml | 3 +- kamon-core/src/main/resources/application.conf | 5 +- kamon-core/src/main/scala/kamon/TraceContext.scala | 21 +++-- .../ActorRefTellInstrumentation.scala | 91 +++++----------------- .../SprayServerInstrumentation.scala | 46 ++++------- .../scala/kamon/newrelic/NewRelicErrorLogger.scala | 19 +++++ .../src/main/scala/kamon/trace/UowTracing.scala | 4 - .../kamon/trace/context/TracingAwareContext.scala | 8 ++ project/Dependencies.scala | 2 +- 9 files changed, 77 insertions(+), 122 deletions(-) create mode 100644 kamon-core/src/main/scala/kamon/newrelic/NewRelicErrorLogger.scala create mode 100644 kamon-core/src/main/scala/kamon/trace/context/TracingAwareContext.scala (limited to 'project') diff --git a/kamon-core/src/main/resources/META-INF/aop.xml b/kamon-core/src/main/resources/META-INF/aop.xml index 79692dd3..efdce792 100644 --- a/kamon-core/src/main/resources/META-INF/aop.xml +++ b/kamon-core/src/main/resources/META-INF/aop.xml @@ -7,9 +7,8 @@ - + - diff --git a/kamon-core/src/main/resources/application.conf b/kamon-core/src/main/resources/application.conf index c87c0ced..1b564f7e 100644 --- a/kamon-core/src/main/resources/application.conf +++ b/kamon-core/src/main/resources/application.conf @@ -1,9 +1,10 @@ akka { loglevel = INFO stdout-loglevel = INFO - log-dead-letters = off + log-dead-letters = on - #extensions = ["kamon.dashboard.DashboardExtension"] + #extensions = ["kamon.dashboard.DashboardExtension"] + akka.loggers = ["kamon.newrelic.NewRelicErrorLogger"] actor { default-dispatcher { diff --git a/kamon-core/src/main/scala/kamon/TraceContext.scala b/kamon-core/src/main/scala/kamon/TraceContext.scala index 73186a18..23da7001 100644 --- a/kamon-core/src/main/scala/kamon/TraceContext.scala +++ b/kamon-core/src/main/scala/kamon/TraceContext.scala @@ -6,6 +6,7 @@ import java.util.concurrent.atomic.AtomicLong import kamon.trace.UowTraceAggregator import scala.concurrent.duration._ import kamon.newrelic.NewRelicReporting +import kamon.trace.UowTracing.Start // TODO: Decide if we need or not an ID, generating it takes time and it doesn't seem necessary. case class TraceContext(id: Long, entries: ActorRef, userContext: Option[Any] = None) { @@ -19,7 +20,13 @@ case class TraceContext(id: Long, entries: ActorRef, userContext: Option[Any] = object TraceContext { val reporter = Kamon.actorSystem.actorOf(Props[NewRelicReporting]) val traceIdCounter = new AtomicLong - def apply()(implicit system: ActorSystem) = new TraceContext(100, system.actorOf(UowTraceAggregator.props(reporter, 30 seconds), "tracer-"+traceIdCounter.incrementAndGet())) // TODO: Move to a kamon specific supervisor, like /user/kamon/tracer + + def apply()(implicit system: ActorSystem) = { + val actor = system.actorOf(UowTraceAggregator.props(reporter, 30 seconds), s"tracer-${traceIdCounter.incrementAndGet()}") + actor ! Start() + + new TraceContext(100, actor) // TODO: Move to a kamon specific supervisor, like /user/kamon/tracer + } } @@ -32,20 +39,10 @@ class TraceAccumulator extends Actor { trait TraceEntry - case class CodeBlockExecutionTime(name: String, begin: Long, end: Long) extends TraceEntry - - - case class TransactionTrace(id: UUID, start: Long, end: Long, entries: Seq[TraceEntry]) - - - - -object Collector { - -} +object Collector trait TraceEntryStorage { def store(entry: TraceEntry): Boolean diff --git a/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala b/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala index df124f41..43841165 100644 --- a/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala +++ b/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala @@ -2,105 +2,54 @@ package kamon.instrumentation import org.aspectj.lang.annotation._ import org.aspectj.lang.ProceedingJoinPoint -import akka.actor.{ActorCell, Props, ActorSystem, ActorRef} -import kamon.{Kamon, Tracer, TraceContext} -import akka.dispatch.{MessageDispatcher, Envelope} +import akka.actor.{Props, ActorSystem, ActorRef} +import kamon.{Tracer, TraceContext} +import akka.dispatch.{Envelope, MessageDispatcher} import com.codahale.metrics.Timer -import kamon.metric.{MetricDirectory, Metrics} import scala.Some -import kamon.instrumentation.SimpleContextPassingInstrumentation.SimpleTraceMessage -import org.slf4j.MDC +import kamon.trace.context.TracingAwareContext case class TraceableMessage(traceContext: Option[TraceContext], message: Any, timer: Timer.Context) +case class DefaultTracingAwareEnvelopeContext(traceContext: Option[TraceContext] = Tracer.context(), timestamp: Long = System.nanoTime) extends TracingAwareContext - -@Aspect -class ActorRefTellInstrumentation { - import ProceedingJoinPointPimp._ - - @Pointcut("execution(* akka.actor.ScalaActorRef+.$bang(..)) && !within(akka.event.Logging.StandardOutLogger) && !within(akka.pattern.PromiseActorRef) && !within(akka.actor.DeadLetterActorRef) && target(actor) && args(message, sender)") - def sendingMessageToActorRef(actor: ActorRef, message: Any, sender: ActorRef) = {} - - @Around("sendingMessageToActorRef(actor, message, sender)") - def around(pjp: ProceedingJoinPoint, actor: ActorRef, message: Any, sender: ActorRef): Unit = { - import kamon.Instrument.instrumentation.sendMessageTransformation - //println(s"====> [$sender] => [$actor] --- $message") - pjp.proceedWithTarget(actor, sendMessageTransformation(sender, actor, message).asInstanceOf[AnyRef], sender) - } -} - - - -@Aspect("""perthis(actorCellCreation(akka.actor.ActorSystem, akka.actor.ActorRef, akka.actor.Props, akka.dispatch.MessageDispatcher, akka.actor.ActorRef))""") +@Aspect("perthis(actorCellCreation(akka.actor.ActorSystem, akka.actor.ActorRef, akka.actor.Props, akka.dispatch.MessageDispatcher, akka.actor.ActorRef))") class ActorCellInvokeInstrumentation { - var instrumentation = ActorReceiveInvokeInstrumentation.noopPreReceive - var self: ActorRef = _ - // AKKA 2.2 introduces the dispatcher parameter. Maybe we could provide a dual pointcut. @Pointcut("execution(akka.actor.ActorCell.new(..)) && args(system, ref, props, dispatcher, parent)") def actorCellCreation(system: ActorSystem, ref: ActorRef, props: Props, dispatcher: MessageDispatcher, parent: ActorRef): Unit = {} - @After("actorCellCreation(system, ref, props, dispatcher, parent)") - def registerMetricsInRegistry(system: ActorSystem, ref: ActorRef, props: Props, dispatcher: MessageDispatcher, parent: ActorRef): Unit = { - instrumentation = kamon.Instrument.instrumentation.receiveInvokeInstrumentation(system, ref, props, dispatcher, parent) - self = ref - } - - @Pointcut("(execution(* akka.actor.ActorCell.invoke(*)) || execution(* akka.routing.RoutedActorCell.sendMessage(*))) && args(envelope)") def invokingActorBehaviourAtActorCell(envelope: Envelope) = {} @Around("invokingActorBehaviourAtActorCell(envelope)") def around(pjp: ProceedingJoinPoint, envelope: Envelope): Unit = { - import ProceedingJoinPointPimp._ - - val (originalEnvelope, ctx) = instrumentation.preReceive(envelope) - //println(s"====>[$ctx] ## [${originalEnvelope.sender}] => [$self] --- ${originalEnvelope.message}") - ctx match { + //safe cast + envelope.asInstanceOf[TracingAwareContext].traceContext match { case Some(c) => { - //MDC.put("uow", c.userContext.get.asInstanceOf[String]) Tracer.set(c) - pjp.proceedWith(originalEnvelope) + pjp.proceed() Tracer.clear - //MDC.remove("uow") } case None => assert(Tracer.context() == None) - pjp.proceedWith(originalEnvelope) + pjp.proceed() } Tracer.clear } } - @Aspect -class UnregisteredActorRefInstrumentation { - @Pointcut("execution(* akka.spray.UnregisteredActorRefBase+.handle(..)) && args(message, sender)") - def sprayResponderHandle(message: Any, sender: ActorRef) = {} +class EnvelopeTracingContext { - @Around("sprayResponderHandle(message, sender)") - def sprayInvokeAround(pjp: ProceedingJoinPoint, message: Any, sender: ActorRef): Unit = { - import ProceedingJoinPointPimp._ - //println("Handling unregistered actor ref message: "+message) + @DeclareMixin("akka.dispatch.Envelope") + def mixin: TracingAwareContext = DefaultTracingAwareEnvelopeContext() - message match { - case SimpleTraceMessage(msg, ctx) => { - ctx match { - case Some(c) => { - Tracer.set(c) - pjp.proceedWith(msg.asInstanceOf[AnyRef]) // TODO: define if we should use Any or AnyRef and unify with the rest of the instrumentation. - Tracer.clear - } - case None => - assert(Tracer.context() == None) - pjp.proceedWith(msg.asInstanceOf[AnyRef]) - } - } - case _ => - //assert(Tracer.context() == None) - pjp.proceed - } + @Pointcut("execution(akka.dispatch.Envelope.new(..)) && this(ctx)") + def requestRecordInit(ctx: TracingAwareContext): Unit = {} + + @After("requestRecordInit(ctx)") + def whenCreatedRequestRecord(ctx: TracingAwareContext): Unit = { + // Necessary to force the initialization of TracingAwareRequestContext at the moment of creation. + ctx.traceContext } } - - diff --git a/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala b/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala index 4eafcebe..0299c4c5 100644 --- a/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala +++ b/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala @@ -2,11 +2,12 @@ package kamon.instrumentation import org.aspectj.lang.annotation.{DeclareMixin, After, Pointcut, Aspect} import kamon.{TraceContext, Tracer} -import kamon.trace.UowTracing.{WebExternal, Finish, Rename} +import kamon.trace.UowTracing.{Finish, Rename} import spray.http.HttpRequest import spray.can.server.{OpenRequest, OpenRequestComponent} -import spray.can.client.HttpHostConnector.RequestContext -import spray.http.HttpHeaders.Host +import kamon.trace.context.TracingAwareContext + +//import spray.can.client.HttpHostConnector.RequestContext trait ContextAware { def traceContext: Option[TraceContext] @@ -41,59 +42,44 @@ class SprayServerInstrumentation { @After("openRequestCreation()") def afterFinishingRequest(): Unit = { - //println("Finishing a request: " + Tracer.context()) +// println("Finishing a request: " + Tracer.context()) Tracer.context().map(_.entries ! Finish()) -/* + if(Tracer.context().isEmpty) { println("WOOOOOPAAAAAAAAA") - }*/ + } } - - - @Pointcut("execution(spray.can.client.HttpHostConnector.RequestContext.new(..)) && this(ctx)") - def requestRecordInit(ctx: TracingAwareRequestContext): Unit = {} + def requestRecordInit(ctx: TracingAwareContext): Unit = {} @After("requestRecordInit(ctx)") - def whenCreatedRequestRecord(ctx: TracingAwareRequestContext): Unit = { + def whenCreatedRequestRecord(ctx: TracingAwareContext): Unit = { // Necessary to force the initialization of TracingAwareRequestContext at the moment of creation. - ctx.context + ctx.traceContext } - - - - - @Pointcut("execution(* spray.can.client.HttpHostConnectionSlot.dispatchToCommander(..)) && args(ctx, msg)") - def requestRecordInit2(ctx: TracingAwareRequestContext, msg: Any): Unit = {} + def requestRecordInit2(ctx: TracingAwareContext, msg: Any): Unit = {} @After("requestRecordInit2(ctx, msg)") - def whenCreatedRequestRecord2(ctx: TracingAwareRequestContext, msg: Any): Unit = { + def whenCreatedRequestRecord2(ctx: TracingAwareContext, msg: Any): Unit = { //println("=======> Spent in WEB External: " + (System.nanoTime() - ctx.timestamp)) // TODO: REMOVE THIS: - val request = (ctx.asInstanceOf[RequestContext]).request +// val request = (ctx.asInstanceOf[RequestContext]).request - ctx.context.map(_.entries ! WebExternal(ctx.timestamp, System.nanoTime(), request.header[Host].map(_.host).getOrElse("UNKNOWN"))) +// ctx.context.map(_.entries ! WebExternal(ctx.timestamp, System.nanoTime(), request.header[Host].map(_.host).getOrElse("UNKNOWN"))) } } -trait TracingAwareRequestContext { - def context: Option[TraceContext] - def timestamp: Long -} - -case class DefaultTracingAwareRequestContext(context: Option[TraceContext] = Tracer.context(), - timestamp: Long = System.nanoTime) extends TracingAwareRequestContext - +case class DefaultTracingAwareRequestContext(traceContext: Option[TraceContext] = Tracer.context(), timestamp: Long = System.nanoTime) extends TracingAwareContext @Aspect class SprayRequestContextTracing { @DeclareMixin("spray.can.client.HttpHostConnector.RequestContext") - def mixin: TracingAwareRequestContext = DefaultTracingAwareRequestContext() + def mixin: TracingAwareContext = DefaultTracingAwareRequestContext() } \ No newline at end of file diff --git a/kamon-core/src/main/scala/kamon/newrelic/NewRelicErrorLogger.scala b/kamon-core/src/main/scala/kamon/newrelic/NewRelicErrorLogger.scala new file mode 100644 index 00000000..72bcb4e2 --- /dev/null +++ b/kamon-core/src/main/scala/kamon/newrelic/NewRelicErrorLogger.scala @@ -0,0 +1,19 @@ +package kamon.newrelic + +import akka.actor.Actor +import akka.event.Logging.Error +import akka.event.Logging.{LoggerInitialized, InitializeLogger} +import com.newrelic.api.agent.NewRelic +import NewRelic.noticeError + +class NewRelicErrorLogger extends Actor { + def receive = { + case InitializeLogger(_) => sender ! LoggerInitialized + case error @ Error(cause, logSource, logClass, message) => notifyError(error) + } + + def notifyError(error: Error): Unit = { + println(error.message) + noticeError(error.cause) + } +} diff --git a/kamon-core/src/main/scala/kamon/trace/UowTracing.scala b/kamon-core/src/main/scala/kamon/trace/UowTracing.scala index 48def942..c794656d 100644 --- a/kamon-core/src/main/scala/kamon/trace/UowTracing.scala +++ b/kamon-core/src/main/scala/kamon/trace/UowTracing.scala @@ -1,12 +1,9 @@ package kamon.trace import akka.actor._ -import kamon.trace.UowTracing.{Start, Finish, Rename} import scala.concurrent.duration.Duration import kamon.trace.UowTracing.Finish import kamon.trace.UowTracing.Rename -import kamon.trace.UowTrace -import kamon.trace.UowTracing.Start import scala.Some sealed trait UowSegment { @@ -29,7 +26,6 @@ case class UowTrace(name: String, segments: Seq[UowSegment]) class UowTraceAggregator(reporting: ActorRef, aggregationTimeout: Duration) extends Actor with ActorLogging { context.setReceiveTimeout(aggregationTimeout) - self ! Start() var name: Option[String] = None var segments: Seq[UowSegment] = Nil diff --git a/kamon-core/src/main/scala/kamon/trace/context/TracingAwareContext.scala b/kamon-core/src/main/scala/kamon/trace/context/TracingAwareContext.scala new file mode 100644 index 00000000..3766dd22 --- /dev/null +++ b/kamon-core/src/main/scala/kamon/trace/context/TracingAwareContext.scala @@ -0,0 +1,8 @@ +package kamon.trace.context + +import kamon.TraceContext + +trait TracingAwareContext { + def traceContext: Option[TraceContext] + def timestamp: Long +} \ No newline at end of file diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 7540ef08..bf139b19 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -7,7 +7,7 @@ object Dependencies { "spray nightlies repo" at "http://nightlies.spray.io" ) - val sprayVersion = "1.2-SNAPSHOT" + val sprayVersion = "1.2-20130801" val akkaVersion = "2.2.0" val sprayCan = "io.spray" % "spray-can" % sprayVersion -- cgit v1.2.3 From 0b62687fd294de343ae90824f4d570e4273586c1 Mon Sep 17 00:00:00 2001 From: Ivan Topolnak Date: Wed, 2 Oct 2013 19:01:00 -0300 Subject: Switched to DynamicVariables, solve context passing issue produced by runnable batching --- kamon-core/src/main/scala/kamon/Kamon.scala | 16 +++------ .../src/main/scala/kamon/TraceContextSwap.scala | 34 ------------------ .../src/main/scala/kamon/executor/eventbus.scala | 2 +- .../ActorRefTellInstrumentation.scala | 16 +++------ .../instrumentation/RunnableInstrumentation.scala | 37 ++++--------------- .../SprayServerInstrumentation.scala | 32 ++--------------- .../src/main/scala/kamon/metric/MetricFilter.scala | 6 ---- .../RunnableInstrumentationSpec.scala | 41 ++++++++++++---------- project/AspectJ.scala | 3 ++ project/Build.scala | 5 +-- project/Dependencies.scala | 2 +- project/Settings.scala | 2 +- 12 files changed, 50 insertions(+), 146 deletions(-) delete mode 100644 kamon-core/src/main/scala/kamon/TraceContextSwap.scala delete mode 100644 kamon-core/src/main/scala/kamon/metric/MetricFilter.scala (limited to 'project') diff --git a/kamon-core/src/main/scala/kamon/Kamon.scala b/kamon-core/src/main/scala/kamon/Kamon.scala index 118239f7..fb1b2393 100644 --- a/kamon-core/src/main/scala/kamon/Kamon.scala +++ b/kamon-core/src/main/scala/kamon/Kamon.scala @@ -6,6 +6,7 @@ import scala.concurrent.duration.FiniteDuration import com.newrelic.api.agent.NewRelic import scala.collection.concurrent.TrieMap import kamon.instrumentation.{SimpleContextPassingInstrumentation, ActorInstrumentationConfiguration} +import scala.util.DynamicVariable object Instrument { @@ -32,20 +33,13 @@ object Kamon { object Tracer { - val ctx = new ThreadLocal[Option[TraceContext]] { - override def initialValue() = None - } + val traceContext = new DynamicVariable[Option[TraceContext]](None) - def context() = ctx.get() - def clear = ctx.remove() - def set(traceContext: TraceContext) = ctx.set(Some(traceContext)) - def start = set(newTraceContext) - def stop = ctx.get match { - case Some(context) => context.close - case None => - } + def context() = traceContext.value + def set(ctx: TraceContext) = traceContext.value = Some(ctx) + def start = set(newTraceContext) def newTraceContext(): TraceContext = TraceContext()(Kamon.actorSystem) } diff --git a/kamon-core/src/main/scala/kamon/TraceContextSwap.scala b/kamon-core/src/main/scala/kamon/TraceContextSwap.scala deleted file mode 100644 index 470b2f34..00000000 --- a/kamon-core/src/main/scala/kamon/TraceContextSwap.scala +++ /dev/null @@ -1,34 +0,0 @@ -package kamon - -import org.slf4j.MDC - -/** - * Provides support for making a TraceContext available as ThreadLocal and cleanning up afterwards. - */ -trait TraceContextSwap { - - def withContext[A](ctx: Option[TraceContext], body: => A): A = withContext(ctx, body, body) - - def withContext[A](ctx: Option[TraceContext], primary: => A, fallback: => A): A = { - - val previous = Tracer.context() - val r = ctx match { - case Some(context) => { - //MDC.put("uow", context.userContext.get.asInstanceOf[String]) - Tracer.set(context) - val bodyResult = primary - //Tracer.clear - //MDC.remove("uow") - - bodyResult - } - case None => fallback - } - previous.map(ctx => Tracer.set(ctx)) - - r - } - -} - -object TraceContextSwap extends TraceContextSwap diff --git a/kamon-core/src/main/scala/kamon/executor/eventbus.scala b/kamon-core/src/main/scala/kamon/executor/eventbus.scala index a1c099d4..d51305a8 100644 --- a/kamon-core/src/main/scala/kamon/executor/eventbus.scala +++ b/kamon-core/src/main/scala/kamon/executor/eventbus.scala @@ -92,7 +92,7 @@ object TryAkka extends App{ threadPrintln("Before doing it") val f = Future { threadPrintln("This is happening inside the future body") } - Tracer.stop + //Thread.sleep(3000) diff --git a/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala b/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala index 6126d642..915f9635 100644 --- a/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala +++ b/kamon-core/src/main/scala/kamon/instrumentation/ActorRefTellInstrumentation.scala @@ -10,7 +10,7 @@ import scala.Some import kamon.trace.context.TracingAwareContext case class TraceableMessage(traceContext: Option[TraceContext], message: Any, timer: Timer.Context) -case class DefaultTracingAwareEnvelopeContext(traceContext: Option[TraceContext] = Tracer.context(), timestamp: Long = System.nanoTime) extends TracingAwareContext +case class DefaultTracingAwareEnvelopeContext(traceContext: Option[TraceContext] = Tracer.traceContext.value, timestamp: Long = System.nanoTime) extends TracingAwareContext @Aspect("perthis(actorCellCreation(akka.actor.ActorSystem, akka.actor.ActorRef, akka.actor.Props, akka.dispatch.MessageDispatcher, akka.actor.ActorRef))") class ActorCellInvokeInstrumentation { @@ -24,17 +24,11 @@ class ActorCellInvokeInstrumentation { @Around("invokingActorBehaviourAtActorCell(envelope)") def around(pjp: ProceedingJoinPoint, envelope: Envelope): Unit = { //safe cast - envelope.asInstanceOf[TracingAwareContext].traceContext match { - case Some(c) => { - Tracer.set(c) - pjp.proceed() - Tracer.clear - } - case None => - //assert(Tracer.context() == None) - pjp.proceed() + val msgContext = envelope.asInstanceOf[TracingAwareContext].traceContext + + Tracer.traceContext.withValue(msgContext) { + pjp.proceed() } - Tracer.clear } } diff --git a/kamon-core/src/main/scala/kamon/instrumentation/RunnableInstrumentation.scala b/kamon-core/src/main/scala/kamon/instrumentation/RunnableInstrumentation.scala index 456917e0..02d74287 100644 --- a/kamon-core/src/main/scala/kamon/instrumentation/RunnableInstrumentation.scala +++ b/kamon-core/src/main/scala/kamon/instrumentation/RunnableInstrumentation.scala @@ -22,7 +22,7 @@ class RunnableInstrumentation { */ @DeclareMixin("scala.concurrent.impl.CallbackRunnable || scala.concurrent.impl.Future.PromiseCompletingRunnable") def onCompleteCallbacksRunnable: TraceContextAwareRunnable = new TraceContextAwareRunnable { - val traceContext: Option[TraceContext] = Tracer.context() + val traceContext: Option[TraceContext] = Tracer.traceContext.value } @@ -38,43 +38,20 @@ class RunnableInstrumentation { - import kamon.TraceContextSwap.withContext - @After("instrumentedRunnableCreation(runnable)") def beforeCreation(runnable: TraceContextAwareRunnable) = { - val x = runnable.traceContext - /*if(runnable.traceContext.isEmpty) - println("WTFWI from: " + (new Throwable).getStackTraceString) - else - println("NOWTF: " + (new Throwable).getStackTraceString)*/ - /* if(traceContext.isEmpty) - println("NO TRACE CONTEXT FOR RUNNABLE at: [[[%s]]]", (new Throwable).getStackTraceString)//println((new Throwable).getStackTraceString) - else - println("SUPER TRACE CONTEXT FOR RUNNABLE at: [[[%s]]]", (new Throwable).getStackTraceString)*/ + // Force traceContext initialization. + runnable.traceContext } @Around("runnableExecution(runnable)") - def around(pjp: ProceedingJoinPoint, runnable: TraceContextAwareRunnable) = { + def around(pjp: ProceedingJoinPoint, runnable: TraceContextAwareRunnable): Any = { import pjp._ - /*println("EXECUTING") - if(runnable.traceContext.isEmpty) - println("NOMONEY") - - runnable.traceContext match { - case Some(context) => { - //MDC.put("uow", context.userContext.get.asInstanceOf[String]) - Tracer.set(context) - val bodyResult = proceed() - Tracer.clear - //MDC.remove("uow") - - bodyResult - } - case None => proceed() - }*/ - withContext(runnable.traceContext, proceed()) + Tracer.traceContext.withValue(runnable.traceContext) { + proceed() + } } } diff --git a/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala b/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala index 06254739..32eabe71 100644 --- a/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala +++ b/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala @@ -17,7 +17,7 @@ trait ContextAware { class SprayOpenRequestContextTracing { @DeclareMixin("spray.can.server.OpenRequestComponent.DefaultOpenRequest") def mixinContextAwareToOpenRequest: ContextAware = new ContextAware { - val traceContext: Option[TraceContext] = Tracer.context() + val traceContext: Option[TraceContext] = Tracer.traceContext.value } } @@ -29,18 +29,9 @@ class SprayServerInstrumentation { @After("openRequestInit(openRequest, enclosing, request, closeAfterResponseCompletion, timestamp)") def afterInit(openRequest: OpenRequest, enclosing: OpenRequestComponent, request: HttpRequest, closeAfterResponseCompletion: Boolean, timestamp: Long): Unit = { - //@After("openRequestInit()") - //def afterInit(): Unit = { Tracer.start val discard = openRequest.asInstanceOf[ContextAware].traceContext - //println("Reply: %s - %s ", Tracer.context().get.id, request.uri.path.toString()) - -// if(discard.isEmpty || discard != Tracer.context()) { -// println("MEGA ERROR") -// } - //openRequest.traceContext - //println("Created the context: " + Tracer.context() + " for the transaction: " + request) Tracer.context().map(_.entries ! Rename(request.uri.path.toString())) } @@ -49,18 +40,13 @@ class SprayServerInstrumentation { @After("openRequestCreation(openRequest)") def afterFinishingRequest(openRequest: OpenRequest): Unit = { -// println("Finishing a request: " + Tracer.context()) val original = openRequest.asInstanceOf[ContextAware].traceContext - println("The original is: " + original + " - " + openRequest.request.uri.path) + Tracer.context().map(_.entries ! Finish()) if(Tracer.context() != original) { println(s"OMG DIFFERENT Original: [${original}] - Came in: [${Tracer.context}]") } - - if(Tracer.context().isEmpty) { - println("WOOOOOPAAAAAAAAA") - } } @Pointcut("execution(spray.can.client.HttpHostConnector.RequestContext.new(..)) && this(ctx)") @@ -71,20 +57,6 @@ class SprayServerInstrumentation { // Necessary to force the initialization of TracingAwareRequestContext at the moment of creation. ctx.traceContext } - - @Pointcut("execution(* spray.can.client.HttpHostConnectionSlot.dispatchToCommander(..)) && args(ctx, msg)") - def requestRecordInit2(ctx: TracingAwareContext, msg: Any): Unit = {} - - @After("requestRecordInit2(ctx, msg)") - def whenCreatedRequestRecord2(ctx: TracingAwareContext, msg: Any): Unit = { - //println("=======> Spent in WEB External: " + (System.nanoTime() - ctx.timestamp)) - - // TODO: REMOVE THIS: -// val request = (ctx.asInstanceOf[RequestContext]).request - -// ctx.context.map(_.entries ! WebExternal(ctx.timestamp, System.nanoTime(), request.header[Host].map(_.host).getOrElse("UNKNOWN"))) - - } } case class DefaultTracingAwareRequestContext(traceContext: Option[TraceContext] = Tracer.context(), timestamp: Long = System.nanoTime) extends TracingAwareContext diff --git a/kamon-core/src/main/scala/kamon/metric/MetricFilter.scala b/kamon-core/src/main/scala/kamon/metric/MetricFilter.scala deleted file mode 100644 index fb117968..00000000 --- a/kamon-core/src/main/scala/kamon/metric/MetricFilter.scala +++ /dev/null @@ -1,6 +0,0 @@ -package kamon.metric - -object MetricFilter { - def actorSystem(system: String): Boolean = !system.startsWith("kamon") - def actor(path: String, system: String): Boolean = true -} diff --git a/kamon-core/src/test/scala/kamon/instrumentation/RunnableInstrumentationSpec.scala b/kamon-core/src/test/scala/kamon/instrumentation/RunnableInstrumentationSpec.scala index 789c7c77..6010a185 100644 --- a/kamon-core/src/test/scala/kamon/instrumentation/RunnableInstrumentationSpec.scala +++ b/kamon-core/src/test/scala/kamon/instrumentation/RunnableInstrumentationSpec.scala @@ -23,39 +23,41 @@ class RunnableInstrumentationSpec extends WordSpec with Matchers with ScalaFutur } } - "should be available during the execution of onComplete callbacks" in { new FutureWithContextFixture { - val onCompleteContext = Promise[TraceContext]() + "should be available during the execution of onComplete callbacks" in new FutureWithContextFixture { + val onCompleteContext = Promise[Option[TraceContext]]() + + Tracer.traceContext.withValue(Some(testContext)) { futureWithContext.onComplete({ - case _ => onCompleteContext.complete(Success(Tracer.context.get)) + case _ => println("Completing second promise from: "+Thread.currentThread().getName + " With Context: " + Tracer.traceContext.value); onCompleteContext.complete(Success(Tracer.traceContext.value)) }) + } - whenReady(onCompleteContext.future) { result => - result should equal(testContext) - } - }} + whenReady(onCompleteContext.future) { result => + result should equal(Some(testContext)) + } + } } } "created in a thread that doest have a TraceContext" must { - "not capture any TraceContext for the body execution" in { new FutureWithoutContextFixture{ - + "not capture any TraceContext for the body execution" in new FutureWithoutContextFixture{ whenReady(futureWithoutContext) { result => result should equal(None) } - }} + } - "not make any TraceContext available during the onComplete callback" in { new FutureWithoutContextFixture { + "not make any TraceContext available during the onComplete callback" in new FutureWithoutContextFixture { val onCompleteContext = Promise[Option[TraceContext]]() - futureWithoutContext.onComplete({ - case _ => onCompleteContext.complete(Success(Tracer.context)) - }) + futureWithoutContext.onComplete { + case _ => onCompleteContext.complete(Success(Tracer.traceContext.value)) + } whenReady(onCompleteContext.future) { result => result should equal(None) } - }} + } } } @@ -68,14 +70,15 @@ class RunnableInstrumentationSpec extends WordSpec with Matchers with ScalaFutur class FutureWithContextFixture { val testContext = TraceContext() - Tracer.set(testContext) - val futureWithContext = Future { Tracer.context } + var futureWithContext: Future[Option[TraceContext]] = _ + Tracer.traceContext.withValue(Some(testContext)) { + futureWithContext = Future { Tracer.traceContext.value } + } } trait FutureWithoutContextFixture { - Tracer.clear // Make sure no TraceContext is available - val futureWithoutContext = Future { Tracer.context } + val futureWithoutContext = Future { Tracer.traceContext.value } } } diff --git a/project/AspectJ.scala b/project/AspectJ.scala index fb5d8bf9..b6c5037a 100644 --- a/project/AspectJ.scala +++ b/project/AspectJ.scala @@ -10,6 +10,9 @@ object AspectJ { compileOnly in Aspectj := true, fork in Test := true, javaOptions in Test <++= weaverOptions in Aspectj, + fork in run := true, + javaOptions in run <++= weaverOptions in Aspectj, + lintProperties in Aspectj += "invalidAbsoluteTypeName = ignore" // Add this line if we need to include some .aj aspects again in the project. diff --git a/project/Build.scala b/project/Build.scala index 80bc5d32..1e5c9a2f 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -19,11 +19,12 @@ object Build extends Build { .settings(basicSettings: _*) .settings(revolverSettings: _*) .settings(aspectJSettings: _*) - .settings(newrelicSettings: _*) + //.settings(newrelicSettings: _*) .settings( libraryDependencies ++= - compile(akkaActor, akkaAgent, sprayCan, sprayClient, sprayRouting, aspectJ, aspectJWeaver, metrics, sprayJson, newrelic) ++ + compile(akkaActor, akkaAgent, aspectJ, aspectJWeaver, metrics, newrelic, sprayJson) ++ + provided(sprayCan, sprayClient, sprayRouting) ++ test(scalatest, akkaTestKit, sprayTestkit)) //.dependsOn(kamonDashboard) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index bf139b19..66852801 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -14,7 +14,7 @@ object Dependencies { val sprayRouting = "io.spray" % "spray-routing" % sprayVersion val sprayTestkit = "io.spray" % "spray-testkit" % sprayVersion val sprayClient = "io.spray" % "spray-client" % sprayVersion - val sprayJson = "io.spray" %% "spray-json" % "1.2.3" + val sprayJson = "io.spray" %% "spray-json" % "1.2.5" val scalaReflect = "org.scala-lang" % "scala-reflect" % "2.10.1" val akkaActor = "com.typesafe.akka" %% "akka-actor" % akkaVersion val akkaAgent = "com.typesafe.akka" %% "akka-agent" % akkaVersion diff --git a/project/Settings.scala b/project/Settings.scala index 6aca813f..9633b405 100644 --- a/project/Settings.scala +++ b/project/Settings.scala @@ -3,7 +3,7 @@ import Keys._ import spray.revolver.RevolverPlugin.Revolver object Settings { - val VERSION = "0.0.2" + val VERSION = "0.0.3" lazy val basicSettings = seq( version := VERSION, -- cgit v1.2.3 From edbdd04d67893b4ebbcee1b1b08bc531219f04a9 Mon Sep 17 00:00:00 2001 From: Ivan Topolnak Date: Thu, 3 Oct 2013 17:20:03 -0300 Subject: Explicit return type added --- .../src/main/scala/kamon/instrumentation/RunnableInstrumentation.scala | 2 +- project/Settings.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'project') diff --git a/kamon-core/src/main/scala/kamon/instrumentation/RunnableInstrumentation.scala b/kamon-core/src/main/scala/kamon/instrumentation/RunnableInstrumentation.scala index 02d74287..992cfa82 100644 --- a/kamon-core/src/main/scala/kamon/instrumentation/RunnableInstrumentation.scala +++ b/kamon-core/src/main/scala/kamon/instrumentation/RunnableInstrumentation.scala @@ -39,7 +39,7 @@ class RunnableInstrumentation { @After("instrumentedRunnableCreation(runnable)") - def beforeCreation(runnable: TraceContextAwareRunnable) = { + def beforeCreation(runnable: TraceContextAwareRunnable): Unit = { // Force traceContext initialization. runnable.traceContext } diff --git a/project/Settings.scala b/project/Settings.scala index 9633b405..356273a6 100644 --- a/project/Settings.scala +++ b/project/Settings.scala @@ -3,7 +3,7 @@ import Keys._ import spray.revolver.RevolverPlugin.Revolver object Settings { - val VERSION = "0.0.3" + val VERSION = "0.0.4" lazy val basicSettings = seq( version := VERSION, -- cgit v1.2.3 From 20f9920d4704e28ee8aa066d151b522c0d14e166 Mon Sep 17 00:00:00 2001 From: Ivan Topolnak Date: Fri, 4 Oct 2013 16:12:29 -0300 Subject: Simplify logging instrumentation and integrate it with kamon-core, kamon-ouw is gone. --- kamon-core/src/main/resources/META-INF/aop.xml | 2 +- kamon-core/src/main/resources/application.conf | 2 +- kamon-core/src/main/resources/logback.xml | 12 +++ kamon-core/src/main/scala/kamon/TraceContext.scala | 8 +- .../ActorLoggingInstrumentation.scala | 32 ++++++ .../SprayServerInstrumentation.scala | 4 +- .../src/main/scala/kamon/trace/UowDirectives.scala | 28 +++++ .../main/scala/test/SimpleRequestProcessor.scala | 14 ++- .../main/scala/kamon/logging/UowActorLogging.scala | 115 --------------------- .../main/scala/kamon/logging/UowDirectives.scala | 30 ------ project/Build.scala | 11 +- project/Dependencies.scala | 3 +- 12 files changed, 91 insertions(+), 170 deletions(-) create mode 100644 kamon-core/src/main/resources/logback.xml create mode 100644 kamon-core/src/main/scala/kamon/instrumentation/ActorLoggingInstrumentation.scala create mode 100644 kamon-core/src/main/scala/kamon/trace/UowDirectives.scala delete mode 100644 kamon-uow/src/main/scala/kamon/logging/UowActorLogging.scala delete mode 100644 kamon-uow/src/main/scala/kamon/logging/UowDirectives.scala (limited to 'project') diff --git a/kamon-core/src/main/resources/META-INF/aop.xml b/kamon-core/src/main/resources/META-INF/aop.xml index 104d7f78..349fc56d 100644 --- a/kamon-core/src/main/resources/META-INF/aop.xml +++ b/kamon-core/src/main/resources/META-INF/aop.xml @@ -14,7 +14,7 @@ - + diff --git a/kamon-core/src/main/resources/application.conf b/kamon-core/src/main/resources/application.conf index 1b564f7e..57f67d32 100644 --- a/kamon-core/src/main/resources/application.conf +++ b/kamon-core/src/main/resources/application.conf @@ -4,7 +4,7 @@ akka { log-dead-letters = on #extensions = ["kamon.dashboard.DashboardExtension"] - akka.loggers = ["kamon.newrelic.NewRelicErrorLogger"] + loggers = ["kamon.newrelic.NewRelicErrorLogger", "akka.event.slf4j.Slf4jLogger"] actor { default-dispatcher { diff --git a/kamon-core/src/main/resources/logback.xml b/kamon-core/src/main/resources/logback.xml new file mode 100644 index 00000000..2ae1e3bd --- /dev/null +++ b/kamon-core/src/main/resources/logback.xml @@ -0,0 +1,12 @@ + + + + %date{HH:mm:ss.SSS} %-5level [%X{uow}][%X{requestId}] [%thread] %logger{55} - %msg%n + + + + + + + + diff --git a/kamon-core/src/main/scala/kamon/TraceContext.scala b/kamon-core/src/main/scala/kamon/TraceContext.scala index 0dfc1630..155b7760 100644 --- a/kamon-core/src/main/scala/kamon/TraceContext.scala +++ b/kamon-core/src/main/scala/kamon/TraceContext.scala @@ -9,13 +9,7 @@ import kamon.newrelic.NewRelicReporting import kamon.trace.UowTracing.Start // TODO: Decide if we need or not an ID, generating it takes time and it doesn't seem necessary. -case class TraceContext(id: Long, entries: ActorRef, userContext: Option[Any] = None) { - //implicit val timeout = Timeout(30, TimeUnit.SECONDS) - implicit val as = Kamon.actorSystem.dispatcher - - def append(entry: TraceEntry) = entries ! entry - def close = entries ! "Close" // TODO type this thing!. -} +case class TraceContext(id: Long, tracer: ActorRef, uow: String = "", userContext: Option[Any] = None) object TraceContext { val reporter = Kamon.actorSystem.actorOf(Props[NewRelicReporting]) diff --git a/kamon-core/src/main/scala/kamon/instrumentation/ActorLoggingInstrumentation.scala b/kamon-core/src/main/scala/kamon/instrumentation/ActorLoggingInstrumentation.scala new file mode 100644 index 00000000..3cf0c6fc --- /dev/null +++ b/kamon-core/src/main/scala/kamon/instrumentation/ActorLoggingInstrumentation.scala @@ -0,0 +1,32 @@ +package kamon.instrumentation + +import org.aspectj.lang.annotation.{Around, Pointcut, DeclareMixin, Aspect} +import kamon.{Tracer, TraceContext} +import org.aspectj.lang.ProceedingJoinPoint +import org.slf4j.MDC + + +@Aspect +class ActorLoggingInstrumentation { + + + @DeclareMixin("akka.event.Logging.LogEvent+") + def traceContextMixin: ContextAware = new ContextAware { + def traceContext: Option[TraceContext] = Tracer.context() + } + + @Pointcut("execution(* akka.event.slf4j.Slf4jLogger.withMdc(..)) && args(logSource, logEvent, logStatement)") + def withMdcInvocation(logSource: String, logEvent: ContextAware, logStatement: () => _): Unit = {} + + @Around("withMdcInvocation(logSource, logEvent, logStatement)") + def putTraceContextInMDC(pjp: ProceedingJoinPoint, logSource: String, logEvent: ContextAware, logStatement: () => _): Unit = { + logEvent.traceContext match { + case Some(ctx) => + MDC.put("uow", ctx.uow) + pjp.proceed() + MDC.remove("uow") + + case None => pjp.proceed() + } + } +} diff --git a/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala b/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala index 32eabe71..5117e7e7 100644 --- a/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala +++ b/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala @@ -32,7 +32,7 @@ class SprayServerInstrumentation { Tracer.start val discard = openRequest.asInstanceOf[ContextAware].traceContext - Tracer.context().map(_.entries ! Rename(request.uri.path.toString())) + Tracer.context().map(_.tracer ! Rename(request.uri.path.toString())) } @Pointcut("execution(* spray.can.server.OpenRequestComponent$DefaultOpenRequest.handleResponseEndAndReturnNextOpenRequest(..)) && target(openRequest)") @@ -42,7 +42,7 @@ class SprayServerInstrumentation { def afterFinishingRequest(openRequest: OpenRequest): Unit = { val original = openRequest.asInstanceOf[ContextAware].traceContext - Tracer.context().map(_.entries ! Finish()) + Tracer.context().map(_.tracer ! Finish()) if(Tracer.context() != original) { println(s"OMG DIFFERENT Original: [${original}] - Came in: [${Tracer.context}]") diff --git a/kamon-core/src/main/scala/kamon/trace/UowDirectives.scala b/kamon-core/src/main/scala/kamon/trace/UowDirectives.scala new file mode 100644 index 00000000..392f53b8 --- /dev/null +++ b/kamon-core/src/main/scala/kamon/trace/UowDirectives.scala @@ -0,0 +1,28 @@ +package kamon.trace + +import spray.routing.directives.BasicDirectives +import spray.routing._ +import kamon.Tracer +import java.util.concurrent.atomic.AtomicLong +import scala.util.Try +import java.net.InetAddress + +trait UowDirectives extends BasicDirectives { + def uow: Directive0 = mapRequest { request => + val uowHeader = request.headers.find(_.name == "X-UOW") + + val generatedUow = uowHeader.map(_.value).getOrElse(UowDirectives.newUow) + Tracer.set(Tracer.context().getOrElse(Tracer.newTraceContext()).copy(uow = generatedUow)) + + request + } +} + +object UowDirectives { + val uowCounter = new AtomicLong + + val hostnamePrefix = Try(InetAddress.getLocalHost.getHostName).getOrElse("unknown-localhost") + + def newUow = "%s-%s".format(hostnamePrefix, uowCounter.incrementAndGet()) + +} \ No newline at end of file diff --git a/kamon-core/src/main/scala/test/SimpleRequestProcessor.scala b/kamon-core/src/main/scala/test/SimpleRequestProcessor.scala index 5b216b39..7d4cec52 100644 --- a/kamon-core/src/main/scala/test/SimpleRequestProcessor.scala +++ b/kamon-core/src/main/scala/test/SimpleRequestProcessor.scala @@ -6,8 +6,9 @@ import spray.routing.SimpleRoutingApp import akka.util.Timeout import spray.httpx.RequestBuilding import scala.concurrent.{Await, Future} +import kamon.trace.UowDirectives -object SimpleRequestProcessor extends App with SimpleRoutingApp with RequestBuilding { +object SimpleRequestProcessor extends App with SimpleRoutingApp with RequestBuilding with UowDirectives { import scala.concurrent.duration._ import spray.client.pipelining._ import akka.pattern.ask @@ -28,11 +29,13 @@ object SimpleRequestProcessor extends App with SimpleRoutingApp with RequestBuil } } ~ path("reply" / Segment) { reqID => - complete { - if (Tracer.context().isEmpty) - println("ROUTE NO CONTEXT") + uow { + complete { + if (Tracer.context().isEmpty) + println("ROUTE NO CONTEXT") - (replier ? reqID).mapTo[String] + (replier ? reqID).mapTo[String] + } } } ~ path("ok") { @@ -78,6 +81,7 @@ class Replier extends Actor with ActorLogging { if(Tracer.context.isEmpty) log.warning("PROCESSING A MESSAGE WITHOUT CONTEXT") + log.info("Processing at the Replier") sender ! anything } } diff --git a/kamon-uow/src/main/scala/kamon/logging/UowActorLogging.scala b/kamon-uow/src/main/scala/kamon/logging/UowActorLogging.scala deleted file mode 100644 index 784bd674..00000000 --- a/kamon-uow/src/main/scala/kamon/logging/UowActorLogging.scala +++ /dev/null @@ -1,115 +0,0 @@ -package kamon.logging - -import akka.actor.{ActorSystem, Actor} -import kamon.{Tracer} -import akka.event.{LoggingBus, LogSource, LoggingAdapter} -import akka.event.Logging._ -import akka.event.slf4j.{Logger, SLF4JLogging} -import akka.event.Logging.Info -import akka.event.Logging.Warning -import akka.event.Logging.Error -import akka.event.Logging.Debug -import org.slf4j.MDC -import akka.util.Helpers - -trait UowActorLogging { - this: Actor => - - val log = { - val (str, clazz) = LogSource(this, context.system) - new ExtendedBusLogging(context.system.eventStream, str, clazz) - } -} - -trait UowLogging { - self: Any => - def system: ActorSystem - - val log = { - val (str, clazz) = LogSource(self.getClass, system) - new ExtendedBusLogging(system.eventStream, str, clazz) - } -} - -class ExtendedBusLogging(val bus: LoggingBus, val logSource: String, val logClass: Class[_]) extends LoggingAdapter { - - import akka.event.Logging._ - - def isErrorEnabled = bus.logLevel >= ErrorLevel - def isWarningEnabled = bus.logLevel >= WarningLevel - def isInfoEnabled = bus.logLevel >= InfoLevel - def isDebugEnabled = bus.logLevel >= DebugLevel - - def currentUow: String = Tracer.context().flatMap(_.userContext).map(_.toString).getOrElse("") - def extras = Map("uow" -> currentUow) - - protected def notifyError(message: String): Unit = bus.publish(Error(logSource, logClass, RichLogEvent(message, extras))) - protected def notifyError(cause: Throwable, message: String): Unit = bus.publish(Error(cause, logSource, logClass, RichLogEvent(message, extras))) - protected def notifyWarning(message: String): Unit = bus.publish(Warning(logSource, logClass, RichLogEvent(message, extras))) - protected def notifyInfo(message: String): Unit = bus.publish(Info(logSource, logClass, RichLogEvent(message, extras))) - protected def notifyDebug(message: String): Unit = bus.publish(Debug(logSource, logClass, RichLogEvent(message, extras))) -} - -case class RichLogEvent(message: String, extras: Map[String, Any]) - - - -class ExtendedSlf4jLogger extends Actor with SLF4JLogging { - - val mdcThreadAttributeName = "sourceThread" - val mdcAkkaSourceAttributeName = "akkaSource" - val mdcAkkaTimestamp = "akkaTimestamp" - - def receive = { - - case event @ Error(cause, logSource, logClass, message) ⇒ - withMdc(logSource, event) { - cause match { - case Error.NoCause | null ⇒ withRichEventProcessing(message) { Logger(logClass, logSource).error(if (message != null) message.toString else null) } - case cause ⇒ withRichEventProcessing(message) { Logger(logClass, logSource).error(if (message != null) message.toString else cause.getLocalizedMessage, cause) } - } - } - - case event @ Warning(logSource, logClass, message) ⇒ - withMdc(logSource, event) { withRichEventProcessing(message) { Logger(logClass, logSource).warn("{}", message.asInstanceOf[AnyRef]) } } - - case event @ Info(logSource, logClass, message) ⇒ - withMdc(logSource, event) { withRichEventProcessing(message) { Logger(logClass, logSource).info("{}", message.asInstanceOf[AnyRef]) } } - - case event @ Debug(logSource, logClass, message) ⇒ - withMdc(logSource, event) { withRichEventProcessing(message) { Logger(logClass, logSource).debug("{}", message.asInstanceOf[AnyRef]) } } - - case InitializeLogger(_) ⇒ - log.info("Slf4jLogger started") - sender ! LoggerInitialized - } - - def withRichEventProcessing(message: Any)(delegate: => Unit): Unit = message match { - case RichLogEvent(event, extras) => { - extras.foreach { case (k, v) => MDC.put(k, v.toString) } - delegate - MDC.clear() - } - case _ => delegate - } - - @inline - final def withMdc(logSource: String, logEvent: LogEvent)(logStatement: ⇒ Unit) { - MDC.put(mdcAkkaSourceAttributeName, logSource) - MDC.put(mdcThreadAttributeName, logEvent.thread.getName) - MDC.put(mdcAkkaTimestamp, formatTimestamp(logEvent.timestamp)) - try logStatement finally { - MDC.remove(mdcAkkaSourceAttributeName) - MDC.remove(mdcThreadAttributeName) - MDC.remove(mdcAkkaTimestamp) - } - } - - /** - * Override this method to provide a differently formatted timestamp - * @param timestamp a "currentTimeMillis"-obtained timestamp - * @return the given timestamp as a UTC String - */ - protected def formatTimestamp(timestamp: Long): String = - Helpers.currentTimeMillisToUTCString(timestamp) -} diff --git a/kamon-uow/src/main/scala/kamon/logging/UowDirectives.scala b/kamon-uow/src/main/scala/kamon/logging/UowDirectives.scala deleted file mode 100644 index 0b54cedc..00000000 --- a/kamon-uow/src/main/scala/kamon/logging/UowDirectives.scala +++ /dev/null @@ -1,30 +0,0 @@ -package kamon.logging - -import java.util.concurrent.atomic.AtomicLong -import spray.routing.Directive0 -import spray.routing.directives.BasicDirectives -import java.net.InetAddress -import scala.util.Try -import kamon.{Tracer, Kamon} - -trait UowDirectives extends BasicDirectives { - def uow: Directive0 = mapRequest { request => - val uowHeader = request.headers.find(_.name == "X-UOW") - - val generatedUow = uowHeader.map(_.value).orElse(Some(UowDirectives.newUow)) - println("Generated UOW: "+generatedUow) - Tracer.set(Tracer.context().getOrElse(Tracer.newTraceContext()).copy(userContext = generatedUow)) - - - request - } -} - -object UowDirectives { - val uowCounter = new AtomicLong - - val hostnamePrefix = Try(InetAddress.getLocalHost.getHostName).getOrElse("unknown-localhost") - - def newUow = "%s-%s".format(hostnamePrefix, uowCounter.incrementAndGet()) - -} diff --git a/project/Build.scala b/project/Build.scala index 1e5c9a2f..022ff787 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -8,7 +8,7 @@ object Build extends Build { import Dependencies._ lazy val root = Project("root", file(".")) - .aggregate(kamonCore, kamonUow, kamonDashboard) + .aggregate(kamonCore, kamonDashboard) .settings(basicSettings: _*) .settings( publish := (), @@ -23,16 +23,11 @@ object Build extends Build { .settings( libraryDependencies ++= - compile(akkaActor, akkaAgent, aspectJ, aspectJWeaver, metrics, newrelic, sprayJson) ++ - provided(sprayCan, sprayClient, sprayRouting) ++ + compile(akkaActor, akkaAgent, aspectJ, aspectJWeaver, metrics, newrelic, sprayJson, logback, akkaSlf4j) ++ + compile(sprayCan, sprayClient, sprayRouting) ++ test(scalatest, akkaTestKit, sprayTestkit)) //.dependsOn(kamonDashboard) - lazy val kamonUow = Project("kamon-uow", file("kamon-uow")) - .settings(basicSettings: _*) - .settings(libraryDependencies ++= compile(akkaActor, akkaSlf4j, sprayRouting)) - .dependsOn(kamonCore) - lazy val kamonDashboard = Project("kamon-dashboard", file("kamon-dashboard")) .settings(basicSettings: _*) .settings(libraryDependencies ++= compile(akkaActor, akkaSlf4j, sprayRouting, sprayCan, sprayJson)) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 66852801..1a49d1d4 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -21,12 +21,13 @@ object Dependencies { val akkaSlf4j = "com.typesafe.akka" %% "akka-slf4j" % akkaVersion val akkaTestKit = "com.typesafe.akka" %% "akka-testkit" % akkaVersion val scalatest = "org.scalatest" % "scalatest_2.10" % "2.0.M6-SNAP22" - val logback = "ch.qos.logback" % "logback-classic" % "1.0.10" + val logback = "ch.qos.logback" % "logback-classic" % "1.0.13" val aspectJ = "org.aspectj" % "aspectjrt" % "1.7.2" val aspectJWeaver = "org.aspectj" % "aspectjweaver" % "1.7.2" val metrics = "com.codahale.metrics" % "metrics-core" % "3.0.0" val newrelic = "com.newrelic.agent.java" % "newrelic-api" % "2.19.0" + def compile (deps: ModuleID*): Seq[ModuleID] = deps map (_ % "compile") def provided (deps: ModuleID*): Seq[ModuleID] = deps map (_ % "provided") def test (deps: ModuleID*): Seq[ModuleID] = deps map (_ % "test") -- cgit v1.2.3 From ab784051c0f7f976780b8a85ac4f4600b6ca91cc Mon Sep 17 00:00:00 2001 From: Ivan Topolnak Date: Fri, 4 Oct 2013 16:42:56 -0300 Subject: release 0.0.5 --- kamon-core/src/main/resources/application.conf | 38 ---- kamon-core/src/main/resources/logback.xml | 12 -- kamon-core/src/main/resources/newrelic.yml | 242 ------------------------- kamon-core/src/main/resources/reference.conf | 11 ++ kamon-core/src/test/resources/logback.xml | 12 ++ kamon-core/src/test/resources/newrelic.yml | 242 +++++++++++++++++++++++++ project/Build.scala | 6 +- project/Settings.scala | 2 +- 8 files changed, 269 insertions(+), 296 deletions(-) delete mode 100644 kamon-core/src/main/resources/application.conf delete mode 100644 kamon-core/src/main/resources/logback.xml delete mode 100644 kamon-core/src/main/resources/newrelic.yml create mode 100644 kamon-core/src/main/resources/reference.conf create mode 100644 kamon-core/src/test/resources/logback.xml create mode 100644 kamon-core/src/test/resources/newrelic.yml (limited to 'project') diff --git a/kamon-core/src/main/resources/application.conf b/kamon-core/src/main/resources/application.conf deleted file mode 100644 index 57f67d32..00000000 --- a/kamon-core/src/main/resources/application.conf +++ /dev/null @@ -1,38 +0,0 @@ -akka { - loglevel = INFO - stdout-loglevel = INFO - log-dead-letters = on - - #extensions = ["kamon.dashboard.DashboardExtension"] - loggers = ["kamon.newrelic.NewRelicErrorLogger", "akka.event.slf4j.Slf4jLogger"] - - actor { - default-dispatcher { - fork-join-executor { - # Min number of threads to cap factor-based parallelism number to - parallelism-min = 2 - - # The parallelism factor is used to determine thread pool size using the - # following formula: ceil(available processors * factor). Resulting size - # is then bounded by the parallelism-min and parallelism-max values. - parallelism-factor = 3.0 - - # Max number of threads to cap factor-based parallelism number to - parallelism-max = 8 - } - - throughput = 100 - } - debug { - unhandled = on - } - } -} - - - - - - - - diff --git a/kamon-core/src/main/resources/logback.xml b/kamon-core/src/main/resources/logback.xml deleted file mode 100644 index 2ae1e3bd..00000000 --- a/kamon-core/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - %date{HH:mm:ss.SSS} %-5level [%X{uow}][%X{requestId}] [%thread] %logger{55} - %msg%n - - - - - - - - diff --git a/kamon-core/src/main/resources/newrelic.yml b/kamon-core/src/main/resources/newrelic.yml deleted file mode 100644 index 1b1ad53b..00000000 --- a/kamon-core/src/main/resources/newrelic.yml +++ /dev/null @@ -1,242 +0,0 @@ -# -# This file configures the New Relic Agent. New Relic monitors -# Java applications with deep visibility and low overhead. For more -# information, visit www.newrelic.com. -# -# This configuration file is custom generated for Ivan Topolnak - ivantopo@gmail.com -# -# This section is for settings common to all environments. -# Do not add anything above this next line. -common: &default_settings - # - # ============================== LICENSE KEY =============================== - - # You must specify the license key associated with your New Relic - # account. This key binds your Agent's data to your account in the - # New Relic service. - license_key: '2e24765acb032cb9e7207013b5ba3e2ab7d2d75c' - - # Agent Enabled - # Use this setting to force the agent to run or not run. - # Default is true. - # agent_enabled: true - - # Set to true to enable support for auto app naming. - # The name of each web app is detected automatically - # and the agent reports data separately for each one. - # This provides a finer-grained performance breakdown for - # web apps in New Relic. - # Default is false. - enable_auto_app_naming: false - - # Set to true to enable component-based transaction naming. - # Set to false to use the URI of a web request as the name of the transaction. - # Default is true. - enable_auto_transaction_naming: true - - # Set the name of your application as you'd like it show up in New Relic. - # if enable_auto_app_naming is false, the agent reports all data to this application. - # Otherwise, the agent reports only background tasks (transactions for non-web applications) to this application. - # To report data to more than one application, separate the application names with ";". - # For example, to report data to"My Application" and "My Application 2" use this: - # app_name: My Application;My Application 2 - # This setting is required. - app_name: My Application - - # The agent uses its own log file to keep its logging - # separate from that of your application. Specify the log level here. - # This setting is dynamic, so changes do not require restarting your application. - # The levels in increasing order of verboseness are: off, severe, warning, info, fine, finer, finest - # Default is info. - log_level: finest - enable_custom_tracing: true - - # Log all data to and from New Relic in plain text. - # This setting is dynamic, so changes do not require restarting your application. - # Default is false. - #audit_mode: true - - # The number of log files to use. - # Default is 1. - #log_file_count: 1 - - # The maximum number of bytes to write to any one log file. - # Default is 0 (no limit). - #log_limit_in_kbytes: 0 - - # The name of the log file. - # Default is newrelic_agent.log. - #log_file_name: newrelic_agent.log - - # The log file directory. - # Default is the logs directory in the newrelic.jar parent directory. - log_file_path: /home/ivantopo/Desktop/tmp - - # The agent communicates with New Relic via https by - # default. If you want to communicate with newrelic via http, - # then turn off SSL by setting this value to false. - # This work is done asynchronously to the threads that process your - # application code, so response times will not be directly affected - # by this change. - # Default is true. - ssl: true - - # Proxy settings for connecting to the New Relic server. - # - # If a proxy is used, the host setting is required. Other settings - # are optional. Default port is 8080. The username and password - # settings will be used to authenticate to Basic Auth challenges - # from a proxy server. - # - # proxy_host: hostname - # proxy_port: 8080 - # proxy_user: username - # proxy_password: password - - # Tells transaction tracer and error collector (when enabled) - # whether or not to capture HTTP params. When true, frameworks can - # exclude HTTP parameters from being captured. - # Default is false. - capture_params: false - - # Tells transaction tracer and error collector to not to collect - # specific http request parameters. - # ignored_params: credit_card, ssn, password - - # Transaction tracer captures deep information about slow - # transactions and sends this to the New Relic service once a - # minute. Included in the transaction is the exact call sequence of - # the transactions including any SQL statements issued. - transaction_tracer: - - # Transaction tracer is enabled by default. Set this to false to - # turn it off. This feature is only available at the higher product levels. - # Default is true. - enabled: true - - # Threshold in seconds for when to collect a transaction - # trace. When the response time of a controller action exceeds - # this threshold, a transaction trace will be recorded and sent to - # New Relic. Valid values are any float value, or (default) "apdex_f", - # which will use the threshold for the "Frustrated" Apdex level - # (greater than four times the apdex_t value). - # Default is apdex_f. - transaction_threshold: apdex_f - - # When transaction tracer is on, SQL statements can optionally be - # recorded. The recorder has three modes, "off" which sends no - # SQL, "raw" which sends the SQL statement in its original form, - # and "obfuscated", which strips out numeric and string literals. - # Default is obfuscated. - record_sql: obfuscated - - # Obfuscate only occurrences of specific SQL fields names. - # This setting only applies if "record_sql" is set to "raw". - #obfuscated_sql_fields: credit_card, ssn, password - - # Set this to true to log SQL statements instead of recording them. - # SQL is logged using the record_sql mode. - # Default is false. - log_sql: false - - # Threshold in seconds for when to collect stack trace for a SQL - # call. In other words, when SQL statements exceed this threshold, - # then capture and send to New Relic the current stack trace. This is - # helpful for pinpointing where long SQL calls originate from. - # Default is 0.5 seconds. - stack_trace_threshold: 0.5 - - # Determines whether the agent will capture query plans for slow - # SQL queries. Only supported for MySQL and PostgreSQL. - # Default is true. - explain_enabled: true - - # Threshold for query execution time below which query plans will not - # not be captured. Relevant only when `explain_enabled` is true. - # Default is 0.5 seconds. - explain_threshold: 0.5 - - # Use this setting to control the variety of transaction traces. - # The higher the setting, the greater the variety. - # Set this to 0 to always report the slowest transaction trace. - # Default is 20. - top_n: 20 - - - # Error collector captures information about uncaught exceptions and - # sends them to New Relic for viewing - error_collector: - - # Error collector is enabled by default. Set this to false to turn - # it off. This feature is only available at the higher product levels. - # Default is true. - enabled: true - - # To stop specific exceptions from reporting to New Relic, set this property - # to a comma separated list of full class names. - # - # ignore_errors: - - # To stop specific http status codes from being reporting to New Relic as errors, - # set this property to a comma separated list of status codes to ignore. - # When this property is commented out it defaults to ignoring 404s. - # - # ignore_status_codes: 404 - - # Cross Application Tracing adds request and response headers to - # external calls using the Apache HttpClient libraries to provided better - # performance data when calling applications monitored by other New Relic Agents. - # - cross_application_tracer: - # Set to true to enable cross application tracing. - # Default is true. - enabled: true - - # Thread profiler measures wall clock time, CPU time, and method call counts - # in your application's threads as they run. - thread_profiler: - - # Set to false to disable the thread profiler. - # Default is true. - enabled: true - - #============================== Browser Monitoring =============================== - # New Relic Real User Monitoring gives you insight into the performance real users are - # experiencing with your website. This is accomplished by measuring the time it takes for - # your users' browsers to download and render your web pages by injecting a small amount - # of JavaScript code into the header and footer of each page. - browser_monitoring: - # By default the agent automatically inserts API calls in compiled JSPs to - # inject the monitoring JavaScript into web pages. - # Set this attribute to false to turn off this behavior. - auto_instrument: true - # Set this attribute to false to prevent injection of the monitoring JavaScript. - # Default is true. - enabled: true - -# Application Environments -# ------------------------------------------ -# Environment specific settings are in this section. -# You can use the environment to override the default settings. -# For example, to change the app_name setting. -# Use -Dnewrelic.environment= on the Java command line -# to set the environment. -# The default environment is production. - -# NOTE if your application has other named environments, you should -# provide configuration settings for these environments here. - -development: - <<: *default_settings - app_name: KAMON[Development] - -test: - <<: *default_settings - app_name: My Application (Test) - -production: - <<: *default_settings - -staging: - <<: *default_settings - app_name: My Application (Staging) \ No newline at end of file diff --git a/kamon-core/src/main/resources/reference.conf b/kamon-core/src/main/resources/reference.conf new file mode 100644 index 00000000..29532595 --- /dev/null +++ b/kamon-core/src/main/resources/reference.conf @@ -0,0 +1,11 @@ +akka { + loggers = ["kamon.newrelic.NewRelicErrorLogger", "akka.event.slf4j.Slf4jLogger"] +} + + + + + + + + diff --git a/kamon-core/src/test/resources/logback.xml b/kamon-core/src/test/resources/logback.xml new file mode 100644 index 00000000..2ae1e3bd --- /dev/null +++ b/kamon-core/src/test/resources/logback.xml @@ -0,0 +1,12 @@ + + + + %date{HH:mm:ss.SSS} %-5level [%X{uow}][%X{requestId}] [%thread] %logger{55} - %msg%n + + + + + + + + diff --git a/kamon-core/src/test/resources/newrelic.yml b/kamon-core/src/test/resources/newrelic.yml new file mode 100644 index 00000000..1b1ad53b --- /dev/null +++ b/kamon-core/src/test/resources/newrelic.yml @@ -0,0 +1,242 @@ +# +# This file configures the New Relic Agent. New Relic monitors +# Java applications with deep visibility and low overhead. For more +# information, visit www.newrelic.com. +# +# This configuration file is custom generated for Ivan Topolnak - ivantopo@gmail.com +# +# This section is for settings common to all environments. +# Do not add anything above this next line. +common: &default_settings + # + # ============================== LICENSE KEY =============================== + + # You must specify the license key associated with your New Relic + # account. This key binds your Agent's data to your account in the + # New Relic service. + license_key: '2e24765acb032cb9e7207013b5ba3e2ab7d2d75c' + + # Agent Enabled + # Use this setting to force the agent to run or not run. + # Default is true. + # agent_enabled: true + + # Set to true to enable support for auto app naming. + # The name of each web app is detected automatically + # and the agent reports data separately for each one. + # This provides a finer-grained performance breakdown for + # web apps in New Relic. + # Default is false. + enable_auto_app_naming: false + + # Set to true to enable component-based transaction naming. + # Set to false to use the URI of a web request as the name of the transaction. + # Default is true. + enable_auto_transaction_naming: true + + # Set the name of your application as you'd like it show up in New Relic. + # if enable_auto_app_naming is false, the agent reports all data to this application. + # Otherwise, the agent reports only background tasks (transactions for non-web applications) to this application. + # To report data to more than one application, separate the application names with ";". + # For example, to report data to"My Application" and "My Application 2" use this: + # app_name: My Application;My Application 2 + # This setting is required. + app_name: My Application + + # The agent uses its own log file to keep its logging + # separate from that of your application. Specify the log level here. + # This setting is dynamic, so changes do not require restarting your application. + # The levels in increasing order of verboseness are: off, severe, warning, info, fine, finer, finest + # Default is info. + log_level: finest + enable_custom_tracing: true + + # Log all data to and from New Relic in plain text. + # This setting is dynamic, so changes do not require restarting your application. + # Default is false. + #audit_mode: true + + # The number of log files to use. + # Default is 1. + #log_file_count: 1 + + # The maximum number of bytes to write to any one log file. + # Default is 0 (no limit). + #log_limit_in_kbytes: 0 + + # The name of the log file. + # Default is newrelic_agent.log. + #log_file_name: newrelic_agent.log + + # The log file directory. + # Default is the logs directory in the newrelic.jar parent directory. + log_file_path: /home/ivantopo/Desktop/tmp + + # The agent communicates with New Relic via https by + # default. If you want to communicate with newrelic via http, + # then turn off SSL by setting this value to false. + # This work is done asynchronously to the threads that process your + # application code, so response times will not be directly affected + # by this change. + # Default is true. + ssl: true + + # Proxy settings for connecting to the New Relic server. + # + # If a proxy is used, the host setting is required. Other settings + # are optional. Default port is 8080. The username and password + # settings will be used to authenticate to Basic Auth challenges + # from a proxy server. + # + # proxy_host: hostname + # proxy_port: 8080 + # proxy_user: username + # proxy_password: password + + # Tells transaction tracer and error collector (when enabled) + # whether or not to capture HTTP params. When true, frameworks can + # exclude HTTP parameters from being captured. + # Default is false. + capture_params: false + + # Tells transaction tracer and error collector to not to collect + # specific http request parameters. + # ignored_params: credit_card, ssn, password + + # Transaction tracer captures deep information about slow + # transactions and sends this to the New Relic service once a + # minute. Included in the transaction is the exact call sequence of + # the transactions including any SQL statements issued. + transaction_tracer: + + # Transaction tracer is enabled by default. Set this to false to + # turn it off. This feature is only available at the higher product levels. + # Default is true. + enabled: true + + # Threshold in seconds for when to collect a transaction + # trace. When the response time of a controller action exceeds + # this threshold, a transaction trace will be recorded and sent to + # New Relic. Valid values are any float value, or (default) "apdex_f", + # which will use the threshold for the "Frustrated" Apdex level + # (greater than four times the apdex_t value). + # Default is apdex_f. + transaction_threshold: apdex_f + + # When transaction tracer is on, SQL statements can optionally be + # recorded. The recorder has three modes, "off" which sends no + # SQL, "raw" which sends the SQL statement in its original form, + # and "obfuscated", which strips out numeric and string literals. + # Default is obfuscated. + record_sql: obfuscated + + # Obfuscate only occurrences of specific SQL fields names. + # This setting only applies if "record_sql" is set to "raw". + #obfuscated_sql_fields: credit_card, ssn, password + + # Set this to true to log SQL statements instead of recording them. + # SQL is logged using the record_sql mode. + # Default is false. + log_sql: false + + # Threshold in seconds for when to collect stack trace for a SQL + # call. In other words, when SQL statements exceed this threshold, + # then capture and send to New Relic the current stack trace. This is + # helpful for pinpointing where long SQL calls originate from. + # Default is 0.5 seconds. + stack_trace_threshold: 0.5 + + # Determines whether the agent will capture query plans for slow + # SQL queries. Only supported for MySQL and PostgreSQL. + # Default is true. + explain_enabled: true + + # Threshold for query execution time below which query plans will not + # not be captured. Relevant only when `explain_enabled` is true. + # Default is 0.5 seconds. + explain_threshold: 0.5 + + # Use this setting to control the variety of transaction traces. + # The higher the setting, the greater the variety. + # Set this to 0 to always report the slowest transaction trace. + # Default is 20. + top_n: 20 + + + # Error collector captures information about uncaught exceptions and + # sends them to New Relic for viewing + error_collector: + + # Error collector is enabled by default. Set this to false to turn + # it off. This feature is only available at the higher product levels. + # Default is true. + enabled: true + + # To stop specific exceptions from reporting to New Relic, set this property + # to a comma separated list of full class names. + # + # ignore_errors: + + # To stop specific http status codes from being reporting to New Relic as errors, + # set this property to a comma separated list of status codes to ignore. + # When this property is commented out it defaults to ignoring 404s. + # + # ignore_status_codes: 404 + + # Cross Application Tracing adds request and response headers to + # external calls using the Apache HttpClient libraries to provided better + # performance data when calling applications monitored by other New Relic Agents. + # + cross_application_tracer: + # Set to true to enable cross application tracing. + # Default is true. + enabled: true + + # Thread profiler measures wall clock time, CPU time, and method call counts + # in your application's threads as they run. + thread_profiler: + + # Set to false to disable the thread profiler. + # Default is true. + enabled: true + + #============================== Browser Monitoring =============================== + # New Relic Real User Monitoring gives you insight into the performance real users are + # experiencing with your website. This is accomplished by measuring the time it takes for + # your users' browsers to download and render your web pages by injecting a small amount + # of JavaScript code into the header and footer of each page. + browser_monitoring: + # By default the agent automatically inserts API calls in compiled JSPs to + # inject the monitoring JavaScript into web pages. + # Set this attribute to false to turn off this behavior. + auto_instrument: true + # Set this attribute to false to prevent injection of the monitoring JavaScript. + # Default is true. + enabled: true + +# Application Environments +# ------------------------------------------ +# Environment specific settings are in this section. +# You can use the environment to override the default settings. +# For example, to change the app_name setting. +# Use -Dnewrelic.environment= on the Java command line +# to set the environment. +# The default environment is production. + +# NOTE if your application has other named environments, you should +# provide configuration settings for these environments here. + +development: + <<: *default_settings + app_name: KAMON[Development] + +test: + <<: *default_settings + app_name: My Application (Test) + +production: + <<: *default_settings + +staging: + <<: *default_settings + app_name: My Application (Staging) \ No newline at end of file diff --git a/project/Build.scala b/project/Build.scala index 022ff787..525cf96c 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -23,9 +23,9 @@ object Build extends Build { .settings( libraryDependencies ++= - compile(akkaActor, akkaAgent, aspectJ, aspectJWeaver, metrics, newrelic, sprayJson, logback, akkaSlf4j) ++ - compile(sprayCan, sprayClient, sprayRouting) ++ - test(scalatest, akkaTestKit, sprayTestkit)) + compile(akkaActor, akkaAgent, aspectJ, aspectJWeaver, metrics, newrelic, sprayJson) ++ + provided(sprayCan, sprayClient, sprayRouting, logback, akkaSlf4j) ++ + test(scalatest, akkaTestKit, sprayTestkit, logback, akkaSlf4j)) //.dependsOn(kamonDashboard) lazy val kamonDashboard = Project("kamon-dashboard", file("kamon-dashboard")) diff --git a/project/Settings.scala b/project/Settings.scala index 356273a6..8db28a7e 100644 --- a/project/Settings.scala +++ b/project/Settings.scala @@ -3,7 +3,7 @@ import Keys._ import spray.revolver.RevolverPlugin.Revolver object Settings { - val VERSION = "0.0.4" + val VERSION = "0.0.5" lazy val basicSettings = seq( version := VERSION, -- cgit v1.2.3 From e148933747e8fde17b6ac324df0dee70b8cb9ebc Mon Sep 17 00:00:00 2001 From: Ivan Topolnak Date: Thu, 10 Oct 2013 18:52:09 -0300 Subject: complete spray client instrumentation with experimental branch --- kamon-core/src/main/resources/META-INF/aop.xml | 1 + kamon-core/src/main/resources/logback.xml | 12 ++++ kamon-core/src/main/scala/kamon/TraceContext.scala | 2 +- .../SprayServerInstrumentation.scala | 82 +++++++++++++++++----- .../scala/kamon/newrelic/NewRelicReporting.scala | 23 ++++-- .../src/main/scala/kamon/trace/UowTracing.scala | 15 +++- .../main/scala/test/SimpleRequestProcessor.scala | 14 +++- kamon-core/src/test/resources/logback.xml | 12 ---- kamon-core/src/test/resources/newrelic.yml | 2 +- project/Build.scala | 2 +- project/Dependencies.scala | 4 +- project/NewRelic.scala | 2 +- 12 files changed, 125 insertions(+), 46 deletions(-) create mode 100644 kamon-core/src/main/resources/logback.xml delete mode 100644 kamon-core/src/test/resources/logback.xml (limited to 'project') diff --git a/kamon-core/src/main/resources/META-INF/aop.xml b/kamon-core/src/main/resources/META-INF/aop.xml index 349fc56d..f13effb9 100644 --- a/kamon-core/src/main/resources/META-INF/aop.xml +++ b/kamon-core/src/main/resources/META-INF/aop.xml @@ -26,6 +26,7 @@ + diff --git a/kamon-core/src/main/resources/logback.xml b/kamon-core/src/main/resources/logback.xml new file mode 100644 index 00000000..2ae1e3bd --- /dev/null +++ b/kamon-core/src/main/resources/logback.xml @@ -0,0 +1,12 @@ + + + + %date{HH:mm:ss.SSS} %-5level [%X{uow}][%X{requestId}] [%thread] %logger{55} - %msg%n + + + + + + + + diff --git a/kamon-core/src/main/scala/kamon/TraceContext.scala b/kamon-core/src/main/scala/kamon/TraceContext.scala index 155b7760..63cdb488 100644 --- a/kamon-core/src/main/scala/kamon/TraceContext.scala +++ b/kamon-core/src/main/scala/kamon/TraceContext.scala @@ -17,7 +17,7 @@ object TraceContext { def apply()(implicit system: ActorSystem) = { val n = traceIdCounter.incrementAndGet() - val actor = system.actorOf(UowTraceAggregator.props(reporter, 5 seconds), s"tracer-${n}") + val actor = system.actorOf(UowTraceAggregator.props(reporter, 30 seconds), s"tracer-${n}") actor ! Start() new TraceContext(n, actor) // TODO: Move to a kamon specific supervisor, like /user/kamon/tracer diff --git a/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala b/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala index 5117e7e7..2bd8643c 100644 --- a/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala +++ b/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala @@ -1,11 +1,14 @@ package kamon.instrumentation -import org.aspectj.lang.annotation.{DeclareMixin, After, Pointcut, Aspect} +import org.aspectj.lang.annotation._ import kamon.{TraceContext, Tracer} -import kamon.trace.UowTracing.{Finish, Rename} -import spray.http.HttpRequest -import spray.can.server.{OpenRequest, OpenRequestComponent} +import kamon.trace.UowTracing._ import kamon.trace.context.TracingAwareContext +import org.aspectj.lang.ProceedingJoinPoint +import spray.http.HttpRequest +import kamon.trace.UowTracing.Finish +import kamon.trace.UowTracing.Rename +import spray.http.HttpHeaders.Host //import spray.can.client.HttpHostConnector.RequestContext @@ -13,35 +16,45 @@ trait ContextAware { def traceContext: Option[TraceContext] } +trait TimedContextAware { + def timestamp: Long + def traceContext: Option[TraceContext] +} + @Aspect class SprayOpenRequestContextTracing { @DeclareMixin("spray.can.server.OpenRequestComponent.DefaultOpenRequest") def mixinContextAwareToOpenRequest: ContextAware = new ContextAware { val traceContext: Option[TraceContext] = Tracer.traceContext.value } + + @DeclareMixin("spray.can.client.HttpHostConnector.RequestContext") + def mixinContextAwareToRequestContext: TimedContextAware = new TimedContextAware { + val timestamp: Long = System.nanoTime() + val traceContext: Option[TraceContext] = Tracer.traceContext.value + } } @Aspect class SprayServerInstrumentation { - @Pointcut("execution(spray.can.server.OpenRequestComponent$DefaultOpenRequest.new(..)) && this(openRequest) && args(enclosing, request, closeAfterResponseCompletion, timestamp)") - def openRequestInit(openRequest: OpenRequest, enclosing: OpenRequestComponent, request: HttpRequest, closeAfterResponseCompletion: Boolean, timestamp: Long): Unit = {} + @Pointcut("execution(spray.can.server.OpenRequestComponent$DefaultOpenRequest.new(..)) && this(openRequest) && args(*, request, *, *)") + def openRequestInit(openRequest: ContextAware, request: HttpRequest): Unit = {} - @After("openRequestInit(openRequest, enclosing, request, closeAfterResponseCompletion, timestamp)") - def afterInit(openRequest: OpenRequest, enclosing: OpenRequestComponent, request: HttpRequest, closeAfterResponseCompletion: Boolean, timestamp: Long): Unit = { + @After("openRequestInit(openRequest, request)") + def afterInit(openRequest: ContextAware, request: HttpRequest): Unit = { Tracer.start - val discard = openRequest.asInstanceOf[ContextAware].traceContext + openRequest.traceContext Tracer.context().map(_.tracer ! Rename(request.uri.path.toString())) } @Pointcut("execution(* spray.can.server.OpenRequestComponent$DefaultOpenRequest.handleResponseEndAndReturnNextOpenRequest(..)) && target(openRequest)") - def openRequestCreation(openRequest: OpenRequest): Unit = {} + def openRequestCreation(openRequest: ContextAware): Unit = {} @After("openRequestCreation(openRequest)") - def afterFinishingRequest(openRequest: OpenRequest): Unit = { - val original = openRequest.asInstanceOf[ContextAware].traceContext - + def afterFinishingRequest(openRequest: ContextAware): Unit = { + val original = openRequest.traceContext Tracer.context().map(_.tracer ! Finish()) if(Tracer.context() != original) { @@ -49,13 +62,46 @@ class SprayServerInstrumentation { } } - @Pointcut("execution(spray.can.client.HttpHostConnector.RequestContext.new(..)) && this(ctx)") - def requestRecordInit(ctx: TracingAwareContext): Unit = {} + @Pointcut("execution(spray.can.client.HttpHostConnector.RequestContext.new(..)) && this(ctx) && args(request, *, *)") + def requestRecordInit(ctx: TimedContextAware, request: HttpRequest): Unit = {} - @After("requestRecordInit(ctx)") - def whenCreatedRequestRecord(ctx: TracingAwareContext): Unit = { + @After("requestRecordInit(ctx, request)") + def whenCreatedRequestRecord(ctx: TimedContextAware, request: HttpRequest): Unit = { // Necessary to force the initialization of TracingAwareRequestContext at the moment of creation. - ctx.traceContext + for{ + tctx <- ctx.traceContext + host <- request.header[Host] + } tctx.tracer ! WebExternalStart(ctx.timestamp, host.host) + } + + + + @Pointcut("execution(* spray.can.client.HttpHostConnectionSlot.dispatchToCommander(..)) && args(requestContext, message)") + def dispatchToCommander(requestContext: TimedContextAware, message: Any): Unit = {} + + @Around("dispatchToCommander(requestContext, message)") + def aroundDispatchToCommander(pjp: ProceedingJoinPoint, requestContext: TimedContextAware, message: Any) = { + println("Completing the request with context: " + requestContext.traceContext) + + Tracer.traceContext.withValue(requestContext.traceContext) { + requestContext.traceContext.map { + tctx => tctx.tracer ! WebExternalFinish(requestContext.timestamp) + } + pjp.proceed() + } + + } + + + @Pointcut("execution(* spray.can.client.HttpHostConnector.RequestContext.copy(..)) && this(old)") + def copyingRequestContext(old: TimedContextAware): Unit = {} + + @Around("copyingRequestContext(old)") + def aroundCopyingRequestContext(pjp: ProceedingJoinPoint, old: TimedContextAware) = { + println("Instrumenting the request context copy.") + Tracer.traceContext.withValue(old.traceContext) { + pjp.proceed() + } } } diff --git a/kamon-core/src/main/scala/kamon/newrelic/NewRelicReporting.scala b/kamon-core/src/main/scala/kamon/newrelic/NewRelicReporting.scala index 31e50cfe..6629e164 100644 --- a/kamon-core/src/main/scala/kamon/newrelic/NewRelicReporting.scala +++ b/kamon-core/src/main/scala/kamon/newrelic/NewRelicReporting.scala @@ -2,8 +2,9 @@ package kamon.newrelic import akka.actor.Actor import kamon.trace.UowTrace -import com.newrelic.api.agent.{Trace, NewRelic} -import kamon.trace.UowTracing.WebExternal +import com.newrelic.api.agent.{Response, Request, Trace, NewRelic} +import kamon.trace.UowTracing.{WebExternal, WebExternalFinish, WebExternalStart} +import java.util class NewRelicReporting extends Actor { @@ -11,7 +12,6 @@ class NewRelicReporting extends Actor { case trace: UowTrace => recordTransaction(trace) } - //@Trace def recordTransaction(uowTrace: UowTrace): Unit = { val time = ((uowTrace.segments.last.timestamp - uowTrace.segments.head.timestamp)/1E9) @@ -20,16 +20,25 @@ class NewRelicReporting extends Actor { NewRelic.recordMetric("HttpDispatcher", time.toFloat) uowTrace.segments.collect { case we: WebExternal => we }.foreach { webExternalTrace => - val external = ((webExternalTrace.end - webExternalTrace.start)/1E9).toFloat - NewRelic.recordMetric(s"External/all", external) - NewRelic.recordMetric(s"External/allWeb", external) + val external = ((webExternalTrace.finish - webExternalTrace.start)/1E9).toFloat NewRelic.recordMetric(s"External/${webExternalTrace.host}/http", external) NewRelic.recordMetric(s"External/${webExternalTrace.host}/all", external) - NewRelic.recordMetric(s"External/${webExternalTrace.host}/http/" + "WebTransaction/Custom" + uowTrace.name, external) + NewRelic.recordMetric(s"External/${webExternalTrace.host}/http/WebTransaction/Custom" + uowTrace.name, external) + } +/* + + val allExternals = uowTrace.segments.collect { case we: WebExternal => we } sortBy(_.timestamp) + + + def measureExternal(segments: Seq[WebExternal]): Long = { } + + NewRelic.recordMetric(s"External/all", external) + NewRelic.recordMetric(s"External/allWeb", external)*/ + } } diff --git a/kamon-core/src/main/scala/kamon/trace/UowTracing.scala b/kamon-core/src/main/scala/kamon/trace/UowTracing.scala index c794656d..9ba3813a 100644 --- a/kamon-core/src/main/scala/kamon/trace/UowTracing.scala +++ b/kamon-core/src/main/scala/kamon/trace/UowTracing.scala @@ -2,8 +2,13 @@ package kamon.trace import akka.actor._ import scala.concurrent.duration.Duration +import kamon.trace.UowTracing._ +import scala.Some +import kamon.trace.UowTracing.WebExternalFinish import kamon.trace.UowTracing.Finish import kamon.trace.UowTracing.Rename +import kamon.trace.UowTrace +import kamon.trace.UowTracing.WebExternalStart import scala.Some sealed trait UowSegment { @@ -18,7 +23,9 @@ object UowTracing { case class Start() extends AutoTimestamp case class Finish() extends AutoTimestamp case class Rename(name: String) extends AutoTimestamp - case class WebExternal(start: Long, end: Long, host: String) extends AutoTimestamp + case class WebExternalStart(id: Long, host: String) extends AutoTimestamp + case class WebExternalFinish(id: Long) extends AutoTimestamp + case class WebExternal(start: Long, finish: Long, host: String) extends AutoTimestamp } case class UowTrace(name: String, segments: Seq[UowSegment]) @@ -30,8 +37,14 @@ class UowTraceAggregator(reporting: ActorRef, aggregationTimeout: Duration) exte var name: Option[String] = None var segments: Seq[UowSegment] = Nil + var pendingExternal = List[WebExternalStart]() + def receive = { case finish: Finish => segments = segments :+ finish; finishTracing() + case wes: WebExternalStart => pendingExternal = pendingExternal :+ wes + case finish @ WebExternalFinish(id) => pendingExternal.find(_.id == id).map(start => { + segments = segments :+ WebExternal(start.timestamp, finish.timestamp, start.host) + }) case Rename(newName) => name = Some(newName) case segment: UowSegment => segments = segments :+ segment case ReceiveTimeout => diff --git a/kamon-core/src/main/scala/test/SimpleRequestProcessor.scala b/kamon-core/src/main/scala/test/SimpleRequestProcessor.scala index 7d4cec52..ef657f24 100644 --- a/kamon-core/src/main/scala/test/SimpleRequestProcessor.scala +++ b/kamon-core/src/main/scala/test/SimpleRequestProcessor.scala @@ -24,8 +24,12 @@ object SimpleRequestProcessor extends App with SimpleRoutingApp with RequestBuil startServer(interface = "localhost", port = 9090) { get { path("test"){ - complete { - pipeline(Get("http://www.despegar.com.ar")).map(r => "Ok") + uow { + complete { + val futures = pipeline(Get("http://10.254.10.57:8000/")).map(r => "Ok") :: pipeline(Get("http://10.254.10.57:8000/")).map(r => "Ok") :: Nil + + Future.sequence(futures).map(l => "Ok") + } } } ~ path("reply" / Segment) { reqID => @@ -45,6 +49,12 @@ object SimpleRequestProcessor extends App with SimpleRoutingApp with RequestBuil dynamic { complete(Future { "OK" }) } + } ~ + path("error") { + complete { + throw new NullPointerException + "okk" + } } } } diff --git a/kamon-core/src/test/resources/logback.xml b/kamon-core/src/test/resources/logback.xml deleted file mode 100644 index 2ae1e3bd..00000000 --- a/kamon-core/src/test/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - %date{HH:mm:ss.SSS} %-5level [%X{uow}][%X{requestId}] [%thread] %logger{55} - %msg%n - - - - - - - - diff --git a/kamon-core/src/test/resources/newrelic.yml b/kamon-core/src/test/resources/newrelic.yml index 1b1ad53b..77923e9c 100644 --- a/kamon-core/src/test/resources/newrelic.yml +++ b/kamon-core/src/test/resources/newrelic.yml @@ -54,7 +54,7 @@ common: &default_settings # Log all data to and from New Relic in plain text. # This setting is dynamic, so changes do not require restarting your application. # Default is false. - #audit_mode: true + audit_mode: true # The number of log files to use. # Default is 1. diff --git a/project/Build.scala b/project/Build.scala index 525cf96c..23880c33 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -24,7 +24,7 @@ object Build extends Build { .settings( libraryDependencies ++= compile(akkaActor, akkaAgent, aspectJ, aspectJWeaver, metrics, newrelic, sprayJson) ++ - provided(sprayCan, sprayClient, sprayRouting, logback, akkaSlf4j) ++ + compile(sprayCan, sprayClient, sprayRouting, logback, akkaSlf4j) ++ test(scalatest, akkaTestKit, sprayTestkit, logback, akkaSlf4j)) //.dependsOn(kamonDashboard) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 1a49d1d4..b940edf6 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -7,7 +7,7 @@ object Dependencies { "spray nightlies repo" at "http://nightlies.spray.io" ) - val sprayVersion = "1.2-20130801" + val sprayVersion = "1.2-M8" val akkaVersion = "2.2.0" val sprayCan = "io.spray" % "spray-can" % sprayVersion @@ -25,7 +25,7 @@ object Dependencies { val aspectJ = "org.aspectj" % "aspectjrt" % "1.7.2" val aspectJWeaver = "org.aspectj" % "aspectjweaver" % "1.7.2" val metrics = "com.codahale.metrics" % "metrics-core" % "3.0.0" - val newrelic = "com.newrelic.agent.java" % "newrelic-api" % "2.19.0" + val newrelic = "com.newrelic.agent.java" % "newrelic-api" % "3.0.1" def compile (deps: ModuleID*): Seq[ModuleID] = deps map (_ % "compile") diff --git a/project/NewRelic.scala b/project/NewRelic.scala index e662d4e3..2fc590cd 100644 --- a/project/NewRelic.scala +++ b/project/NewRelic.scala @@ -8,6 +8,6 @@ object NewRelic { lazy val newrelicSettings = SbtNewrelic.newrelicSettings ++ Seq( javaOptions in run <++= jvmOptions in newrelic, - newrelicVersion in newrelic := "2.20.0" + newrelicVersion in newrelic := "3.0.1" ) } -- cgit v1.2.3 From 808846aaa931c2890016d7bb96ad22fd599f4104 Mon Sep 17 00:00:00 2001 From: Ivan Topolnak Date: Wed, 23 Oct 2013 18:04:15 -0300 Subject: Update to spray 1.2RC1 --- .../scala/kamon/instrumentation/SprayServerInstrumentation.scala | 2 +- kamon-core/src/main/scala/kamon/trace/UowTracing.scala | 8 +------- project/Dependencies.scala | 4 ++-- project/Settings.scala | 4 ++-- project/plugins.sbt | 2 ++ 5 files changed, 8 insertions(+), 12 deletions(-) (limited to 'project') diff --git a/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala b/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala index 2bd8643c..2239f382 100644 --- a/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala +++ b/kamon-core/src/main/scala/kamon/instrumentation/SprayServerInstrumentation.scala @@ -62,7 +62,7 @@ class SprayServerInstrumentation { } } - @Pointcut("execution(spray.can.client.HttpHostConnector.RequestContext.new(..)) && this(ctx) && args(request, *, *)") + @Pointcut("execution(spray.can.client.HttpHostConnector.RequestContext.new(..)) && this(ctx) && args(request, *, *, *)") def requestRecordInit(ctx: TimedContextAware, request: HttpRequest): Unit = {} @After("requestRecordInit(ctx, request)") diff --git a/kamon-core/src/main/scala/kamon/trace/UowTracing.scala b/kamon-core/src/main/scala/kamon/trace/UowTracing.scala index 9ba3813a..b09478cc 100644 --- a/kamon-core/src/main/scala/kamon/trace/UowTracing.scala +++ b/kamon-core/src/main/scala/kamon/trace/UowTracing.scala @@ -3,13 +3,6 @@ package kamon.trace import akka.actor._ import scala.concurrent.duration.Duration import kamon.trace.UowTracing._ -import scala.Some -import kamon.trace.UowTracing.WebExternalFinish -import kamon.trace.UowTracing.Finish -import kamon.trace.UowTracing.Rename -import kamon.trace.UowTrace -import kamon.trace.UowTracing.WebExternalStart -import scala.Some sealed trait UowSegment { def timestamp: Long @@ -54,6 +47,7 @@ class UowTraceAggregator(reporting: ActorRef, aggregationTimeout: Duration) exte def finishTracing(): Unit = { reporting ! UowTrace(name.getOrElse("UNKNOWN"), segments) + println("Recorded Segments: " + segments) context.stop(self) } } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index b940edf6..90336c02 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -7,8 +7,8 @@ object Dependencies { "spray nightlies repo" at "http://nightlies.spray.io" ) - val sprayVersion = "1.2-M8" - val akkaVersion = "2.2.0" + val sprayVersion = "1.2-RC1" + val akkaVersion = "2.2.3" val sprayCan = "io.spray" % "spray-can" % sprayVersion val sprayRouting = "io.spray" % "spray-routing" % sprayVersion diff --git a/project/Settings.scala b/project/Settings.scala index 8db28a7e..f7f07527 100644 --- a/project/Settings.scala +++ b/project/Settings.scala @@ -3,12 +3,12 @@ import Keys._ import spray.revolver.RevolverPlugin.Revolver object Settings { - val VERSION = "0.0.5" + val VERSION = "0.0.6" lazy val basicSettings = seq( version := VERSION, organization := "kamon", - scalaVersion := "2.10.2", + scalaVersion := "2.10.3", resolvers ++= Dependencies.resolutionRepos, fork in run := true, scalacOptions := Seq( diff --git a/project/plugins.sbt b/project/plugins.sbt index 6dc4b037..70e313e9 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -7,3 +7,5 @@ addSbtPlugin("io.spray" % "sbt-revolver" % "0.6.2") addSbtPlugin("com.typesafe.sbt" % "sbt-aspectj" % "0.9.0") addSbtPlugin("com.ivantopo.sbt" % "sbt-newrelic" % "0.0.1") + +addSbtPlugin("com.github.gseitz" % "sbt-release" % "0.8") -- cgit v1.2.3