diff options
25 files changed, 310 insertions, 102 deletions
diff --git a/kamon-core/src/main/scala/kamon/instrumentation/scala/FutureInstrumentation.scala b/kamon-core/src/main/scala/kamon/instrumentation/scala/FutureInstrumentation.scala index d8f2b620..bda2da78 100644 --- a/kamon-core/src/main/scala/kamon/instrumentation/scala/FutureInstrumentation.scala +++ b/kamon-core/src/main/scala/kamon/instrumentation/scala/FutureInstrumentation.scala @@ -40,9 +40,9 @@ class FutureInstrumentation { @Around("futureRelatedRunnableExecution(runnable)") def aroundExecution(pjp: ProceedingJoinPoint, runnable: TraceContextAware): Any = { - TraceRecorder.withTraceContext(runnable.traceContext) { + TraceRecorder.withInlineTraceContextReplacement(runnable.traceContext) { pjp.proceed() } } -}
\ No newline at end of file +} diff --git a/kamon-core/src/main/scala/kamon/trace/TraceRecorder.scala b/kamon-core/src/main/scala/kamon/trace/TraceRecorder.scala index efe08cdb..0b3118ed 100644 --- a/kamon-core/src/main/scala/kamon/trace/TraceRecorder.scala +++ b/kamon-core/src/main/scala/kamon/trace/TraceRecorder.scala @@ -16,7 +16,10 @@ package kamon.trace +import scala.language.experimental.macros import java.util.concurrent.atomic.AtomicLong +import kamon.macros.InlineTraceContextMacro + import scala.util.Try import java.net.InetAddress import akka.actor.ActorSystem @@ -66,6 +69,8 @@ object TraceRecorder { try thunk finally setContext(oldContext) } + def withInlineTraceContextReplacement[T](traceCtx: Option[TraceContext])(thunk: ⇒ T): T = macro InlineTraceContextMacro.withInlineTraceContextImpl[T, Option[TraceContext]] + def finish(metadata: Map[String, String] = Map.empty): Unit = currentContext.map(_.finish(metadata)) } diff --git a/kamon-examples/kamon-play-newrelic-example/app/controllers/NewRelicExample.scala b/kamon-examples/kamon-play-example/app/Global.scala index 6b89f268..5fbb9c7e 100644 --- a/kamon-examples/kamon-play-newrelic-example/app/controllers/NewRelicExample.scala +++ b/kamon-examples/kamon-play-example/app/Global.scala @@ -13,25 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. * ========================================================== */ -package controllers -import play.api.mvc.{Action, Controller} -import play.api.libs.concurrent.Execution.Implicits.defaultContext -import scala.concurrent._ +import filters.TraceLocalFilter +import play.api.mvc.WithFilters -object NewRelicExample extends Controller { +object Global extends WithFilters(TraceLocalFilter){ - def sayHelloKamon() = Action.async { - Future { - play.Logger.info("Say hello to Kamon") - Ok("Say hello to Kamon") - } - } - - def sayHelloNewRelic() = Action.async { - Future { - play.Logger.info("Say hello to NewRelic") - Ok("Say hello to NewRelic") - } - } } + + + diff --git a/kamon-examples/kamon-play-example/app/controllers/KamonPlayExample.scala b/kamon-examples/kamon-play-example/app/controllers/KamonPlayExample.scala new file mode 100644 index 00000000..2b2e9373 --- /dev/null +++ b/kamon-examples/kamon-play-example/app/controllers/KamonPlayExample.scala @@ -0,0 +1,71 @@ +/* =================================================== + * Copyright © 2013-2014 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================== */ +package controllers + +import kamon.play.action.TraceName +import play.api.Logger +import play.api.libs.concurrent.Execution.Implicits.defaultContext +import play.api.mvc.{Action, Controller} + +import scala.concurrent._ + +/** + * In order to run the example we need set the -agent parameter to the JVM but Play have some limitations when trying to set an + * java agent in Play dev mode (ie, play run) -> https://github.com/playframework/playframework/issues/1372, so we have others options: + * + * The first option is set -javaagent: path-to-aspectj-weaver in your IDE or + * + * Run the following commands from console: + * + * 1- play stage + * 2- cd target/universal/stage + * 3- java -cp ".:lib/*" -javaagent:lib/org.aspectj.aspectjweaver-1.8.1.jar play.core.server.NettyServer + * + * and finally for test: + * + * curl -i -H 'X-Trace-Token:kamon-test' -H 'MyTraceLocalStorageKey:extra-header' -X GET "http://localhost:9000/helloKamon" + * + * we should get: + * HTTP/1.1 200 OK + * Content-Type: text/plain; charset=utf-8 + * MyTraceLocalStorageKey: extra-header -> Extra Information + * X-Trace-Token: kamon-test -> default Trace-Token + * + * Say hello to Kamon + */*/ + + +object KamonPlayExample extends Controller { + + val logger = Logger(this.getClass) + + def sayHello() = Action.async { + Future { + logger.info("Say hello to Kamon") + Ok("Say hello to Kamon") + } + } + + //using the Kamon TraceName Action to rename the trace name in metrics + def sayHelloWithTraceName() = TraceName("my-trace-name") { + Action.async { + Future { + logger.info("Say hello to Kamon") + Ok("Say hello to Kamon") + } + } + } +} diff --git a/kamon-examples/kamon-play-example/app/filters/TraceLocalFilter.scala b/kamon-examples/kamon-play-example/app/filters/TraceLocalFilter.scala new file mode 100644 index 00000000..08ea782c --- /dev/null +++ b/kamon-examples/kamon-play-example/app/filters/TraceLocalFilter.scala @@ -0,0 +1,52 @@ +/* =================================================== + * Copyright © 2013-2014 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================== */ + +package filters + +import kamon.trace.{TraceRecorder, TraceLocal} +import play.api.Logger +import play.api.mvc.{Result, RequestHeader, Filter} +import play.api.libs.concurrent.Execution.Implicits.defaultContext +import scala.concurrent.Future + +object TraceLocalKey extends TraceLocal.TraceLocalKey { + type ValueType = String +} + +/* + By default Kamon spreads the trace-token-header-name but sometimes is necessary pass through the application requests with some infomation like + extra headers, with kamon it's possible using TraceLocalStorage, in Play applications we can do an Action Filter or using Action Composition, + in this example we are using a simple filter where given a Header store the value and then put the value in the result headers.. + + More detailed usage of TraceLocalStorage: https://github.com/kamon-io/Kamon/blob/b17539d231da923ea854c01d2c69eb02ef1e85b1/kamon-core/src/test/scala/kamon/trace/TraceLocalSpec.scala + */ +object TraceLocalFilter extends Filter { + val logger = Logger(this.getClass) + val TraceLocalStorageKey = "MyTraceLocalStorageKey" + + override def apply(next: (RequestHeader) ⇒ Future[Result])(header: RequestHeader): Future[Result] = { + TraceRecorder.withTraceContext(TraceRecorder.currentContext) { + + TraceLocal.store(TraceLocalKey)(header.headers.get(TraceLocalStorageKey).getOrElse("unknown")) + + next(header).map { + val traceTokenValue = TraceLocal.retrieve(TraceLocalKey).getOrElse("unknown") + logger.info(s"traceTokenValue: $traceTokenValue") + result ⇒ result.withHeaders((TraceLocalStorageKey -> traceTokenValue)) + } + } + } +} diff --git a/kamon-examples/kamon-play-example/conf/application.conf b/kamon-examples/kamon-play-example/conf/application.conf new file mode 100644 index 00000000..4f9a60ec --- /dev/null +++ b/kamon-examples/kamon-play-example/conf/application.conf @@ -0,0 +1,61 @@ +#kamon related configuration +akka { + extensions = ["kamon.statsd.StatsD"] +} + +kamon { + + statsd { + # Hostname and port in which your StatsD is running. Remember that StatsD packets are sent using UDP and + # setting unreachable hosts and/or not open ports wont be warned by the Kamon, your data wont go anywhere. + hostname = "127.0.0.1" + port = 8125 + + # Interval between metrics data flushes to StatsD. It's value must be equal or greater than the + # kamon.metrics.tick-interval setting. + flush-interval = 1 second + + # Max packet size for UDP metrics data sent to StatsD. + max-packet-size = 1024 bytes + + # Subscription patterns used to select which metrics will be pushed to StatsD. Note that first, metrics + # collection for your desired entities must be activated under the kamon.metrics.filters settings. + includes { + actor = [ "*" ] + trace = [ "*" ] + dispatcher = [ "*" ] + } + + simple-metric-key-generator { + # Application prefix for all metrics pushed to StatsD. The default namespacing scheme for metrics follows + # this pattern: + # application.host.entity.entity-name.metric-name + application = "kamon" + } + } + + play { + include-trace-token-header = true + trace-token-header-name = "X-Trace-Token" + } +} + +# This is the main configuration file for the application. +# ~~~~~ + +# Secret key +# ~~~~~ +# The secret key is used to secure cryptographics functions. +# If you deploy your application to several instances be sure to use the same key! +application.secret="3BLM`<aD^5r/L[MinNdw8Tp@915n0djY[g66OSOLi@?k`>AZE9EOphrmf;;6JsAN" + +# The application languages +# ~~~~~ +application.langs="en" + +# Global object class +# ~~~~~ +# Define the Global object class for this application. +# Default to Global in the root package. +# application.global=Global + diff --git a/kamon-examples/kamon-play-newrelic-example/conf/logger.xml b/kamon-examples/kamon-play-example/conf/logger.xml index 84126e9d..84126e9d 100644 --- a/kamon-examples/kamon-play-newrelic-example/conf/logger.xml +++ b/kamon-examples/kamon-play-example/conf/logger.xml diff --git a/kamon-examples/kamon-play-example/conf/routes b/kamon-examples/kamon-play-example/conf/routes new file mode 100644 index 00000000..122c355a --- /dev/null +++ b/kamon-examples/kamon-play-example/conf/routes @@ -0,0 +1,3 @@ +# Routes +GET /helloKamon controllers.KamonPlayExample.sayHello +GET /helloKamonWithTraceName controllers.KamonPlayExample.sayHelloWithTraceName
\ No newline at end of file diff --git a/kamon-examples/kamon-play-example/project/Build.scala b/kamon-examples/kamon-play-example/project/Build.scala new file mode 100644 index 00000000..c348862a --- /dev/null +++ b/kamon-examples/kamon-play-example/project/Build.scala @@ -0,0 +1,48 @@ +import java.io.File +import sbt._ +import Keys._ +import play.Play.autoImport._ +import sbt.Keys._ +import sbt._ +import com.typesafe.sbt.web.SbtWeb + + +object ApplicationBuild extends Build { + + val appName = "Kamon-Play-Example" + val appVersion = "1.0-SNAPSHOT" + + val resolutionRepos = Seq( + "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/", + "Sonatype Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots", + "Sonatype Releases" at "https://oss.sonatype.org/content/repositories/releases", + "Kamon Repository Snapshots" at "http://snapshots.kamon.io" + ) + + val defaultSettings = Seq( + scalaVersion := "2.11.0", + resolvers ++= resolutionRepos, + scalacOptions := Seq( + "-encoding", + "utf8", + "-g:vars", + "-feature", + "-unchecked", + "-deprecation", + "-target:jvm-1.6", + "-language:postfixOps", + "-language:implicitConversions", + "-Xlog-reflective-calls" + )) + + val dependencies = Seq( + "io.kamon" %% "kamon-core" % "0.3.1", + "io.kamon" %% "kamon-play" % "0.3.1", + "io.kamon" %% "kamon-statsd" % "0.3.1", + "org.aspectj" % "aspectjweaver" % "1.8.1" + ) + + val main = Project(appName, file(".")).enablePlugins(play.PlayScala, SbtWeb) + .settings(libraryDependencies ++= dependencies) + .settings(defaultSettings: _*) +} diff --git a/kamon-examples/kamon-play-example/project/build.properties b/kamon-examples/kamon-play-example/project/build.properties new file mode 100644 index 00000000..be6c454f --- /dev/null +++ b/kamon-examples/kamon-play-example/project/build.properties @@ -0,0 +1 @@ +sbt.version=0.13.5 diff --git a/kamon-examples/kamon-play-newrelic-example/project/plugins.sbt b/kamon-examples/kamon-play-example/project/plugins.sbt index 065c342e..adc5b325 100644 --- a/kamon-examples/kamon-play-newrelic-example/project/plugins.sbt +++ b/kamon-examples/kamon-play-example/project/plugins.sbt @@ -5,5 +5,5 @@ logLevel := Level.Warn resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" // Use the Play sbt plugin for Play projects -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.2.1") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.1") diff --git a/kamon-examples/kamon-play-newrelic-example/public/images/favicon.png b/kamon-examples/kamon-play-example/public/images/favicon.png Binary files differindex c7d92d2a..c7d92d2a 100644 --- a/kamon-examples/kamon-play-newrelic-example/public/images/favicon.png +++ b/kamon-examples/kamon-play-example/public/images/favicon.png diff --git a/kamon-examples/kamon-play-newrelic-example/public/images/glyphicons-halflings-white.png b/kamon-examples/kamon-play-example/public/images/glyphicons-halflings-white.png Binary files differindex 3bf6484a..3bf6484a 100644 --- a/kamon-examples/kamon-play-newrelic-example/public/images/glyphicons-halflings-white.png +++ b/kamon-examples/kamon-play-example/public/images/glyphicons-halflings-white.png diff --git a/kamon-examples/kamon-play-newrelic-example/public/images/glyphicons-halflings.png b/kamon-examples/kamon-play-example/public/images/glyphicons-halflings.png Binary files differindex a9969993..a9969993 100644 --- a/kamon-examples/kamon-play-newrelic-example/public/images/glyphicons-halflings.png +++ b/kamon-examples/kamon-play-example/public/images/glyphicons-halflings.png diff --git a/kamon-examples/kamon-play-newrelic-example/public/javascripts/jquery-1.9.0.min.js b/kamon-examples/kamon-play-example/public/javascripts/jquery-1.9.0.min.js index 50d1b22f..50d1b22f 100644 --- a/kamon-examples/kamon-play-newrelic-example/public/javascripts/jquery-1.9.0.min.js +++ b/kamon-examples/kamon-play-example/public/javascripts/jquery-1.9.0.min.js diff --git a/kamon-examples/kamon-play-newrelic-example/public/stylesheets/bootstrap.css b/kamon-examples/kamon-play-example/public/stylesheets/bootstrap.css index 2f56af33..2f56af33 100644 --- a/kamon-examples/kamon-play-newrelic-example/public/stylesheets/bootstrap.css +++ b/kamon-examples/kamon-play-example/public/stylesheets/bootstrap.css diff --git a/kamon-examples/kamon-play-newrelic-example/public/stylesheets/main.css b/kamon-examples/kamon-play-example/public/stylesheets/main.css index e69de29b..e69de29b 100644 --- a/kamon-examples/kamon-play-newrelic-example/public/stylesheets/main.css +++ b/kamon-examples/kamon-play-example/public/stylesheets/main.css diff --git a/kamon-examples/kamon-play-newrelic-example/conf/application.conf b/kamon-examples/kamon-play-newrelic-example/conf/application.conf deleted file mode 100644 index 0156d3b4..00000000 --- a/kamon-examples/kamon-play-newrelic-example/conf/application.conf +++ /dev/null @@ -1,51 +0,0 @@ -#kamon related configuration - -akka { - extensions = ["kamon.newrelic.NewRelic"] -} - -kamon { - - newrelic { - app-name = "KamonNewRelicExample[Development]" - license-key = <<Your Key>> - } - - play { - include-trace-token-header = true - trace-token-header-name = "X-Trace-Token" - } -} - -# This is the main configuration file for the application. -# ~~~~~ - -# Secret key -# ~~~~~ -# The secret key is used to secure cryptographics functions. -# If you deploy your application to several instances be sure to use the same key! -application.secret="3BLM`<aD^5r/L[MinNdw8Tp@915n0djY[g66OSOLi@?k`>AZE9EOphrmf;;6JsAN" - -# The application languages -# ~~~~~ -application.langs="en" - -# Global object class -# ~~~~~ -# Define the Global object class for this application. -# Default to Global in the root package. -# application.global=Global - -# Logger -# ~~~~~ -# You can also configure logback (http://logback.qos.ch/), by providing a logger.xml file in the conf directory . - -# Root logger: -logger.root=ERROR - -# Logger used by the framework: -logger.play=INFO - -# Logger provided to your application: -logger.application=DEBUG - diff --git a/kamon-examples/kamon-play-newrelic-example/conf/routes b/kamon-examples/kamon-play-newrelic-example/conf/routes deleted file mode 100644 index c1c9d64b..00000000 --- a/kamon-examples/kamon-play-newrelic-example/conf/routes +++ /dev/null @@ -1,3 +0,0 @@ -# Routes -GET /helloKamon controllers.NewRelicExample.sayHelloKamon -GET /helloNewRelic controllers.NewRelicExample.sayHelloNewRelic diff --git a/kamon-examples/kamon-play-newrelic-example/project/Build.scala b/kamon-examples/kamon-play-newrelic-example/project/Build.scala deleted file mode 100644 index fa240c39..00000000 --- a/kamon-examples/kamon-play-newrelic-example/project/Build.scala +++ /dev/null @@ -1,22 +0,0 @@ -import java.io.File -import sbt._ -import Keys._ -import play.Project._ - -object ApplicationBuild extends Build { - - val appName = "Kamon-Play-NewRelic-Example" - val appVersion = "1.0-SNAPSHOT" - - val appDependencies = Seq( - "kamon" % "kamon-core" % "0.0.14-SNAPSHOT", - "kamon" % "kamon-play" % "0.0.14-SNAPSHOT", - "kamon" % "kamon-newrelic" % "0.0.14-SNAPSHOT" - ) - - - val main = play.Project(appName, appVersion, appDependencies).settings( - // For additionally resolving from the conventional ivy local home. - resolvers += Resolver.file("LocalIvy", file(Path.userHome + - File.separator + ".ivy2" + File.separator + "local"))(Resolver.ivyStylePatterns)) -} diff --git a/kamon-examples/kamon-play-newrelic-example/project/build.properties b/kamon-examples/kamon-play-newrelic-example/project/build.properties deleted file mode 100644 index 0974fce4..00000000 --- a/kamon-examples/kamon-play-newrelic-example/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=0.13.0 diff --git a/kamon-macros/src/main/scala/kamon/macros/InlineTraceContextMacro.scala b/kamon-macros/src/main/scala/kamon/macros/InlineTraceContextMacro.scala new file mode 100644 index 00000000..ea8cc7b8 --- /dev/null +++ b/kamon-macros/src/main/scala/kamon/macros/InlineTraceContextMacro.scala @@ -0,0 +1,46 @@ +/* + * ========================================================================================= + * Copyright © 2013 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + * ========================================================================================= + */ + +package kamon.macros + +import scala.language.experimental.macros +import scala.reflect.macros.Context + +object InlineTraceContextMacro { + + // Macro to generate an inline version of kamon.trace.TraceRecorder.withTraceContext + def withInlineTraceContextImpl[T: c.WeakTypeTag, TC: c.WeakTypeTag](c: Context)(traceCtx: c.Expr[TC])(thunk: c.Expr[T]) = { + import c.universe._ + + val inlineThunk = + Block( + List( + ValDef( + Modifiers(), newTermName("oldContext"), TypeTree(), + Select(Ident(newTermName("TraceRecorder")), newTermName("currentContext"))), + Apply( + Select(Ident(newTermName("TraceRecorder")), newTermName("setContext")), + List(traceCtx.tree))), + Try( + thunk.tree, + List(), + Apply( + Select(Ident(newTermName("TraceRecorder")), newTermName("setContext")), + List(Ident(newTermName("oldContext")))))) + + c.Expr[T](inlineThunk) + } +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 667f8dce..4a2f2276 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -35,7 +35,7 @@ object Dependencies { val slf4Api = "org.slf4j" % "slf4j-api" % slf4jVersion val slf4nop = "org.slf4j" % "slf4j-nop" % slf4jVersion val jsr166 = "io.gatling" % "jsr166e" % "1.0" - + val scalaCompiler = "org.scala-lang" % "scala-compiler" % Settings.ScalaVersion def compile (deps: ModuleID*): Seq[ModuleID] = deps map (_ % "compile") def provided (deps: ModuleID*): Seq[ModuleID] = deps map (_ % "provided") diff --git a/project/Projects.scala b/project/Projects.scala index 5d747e49..1043ae58 100644 --- a/project/Projects.scala +++ b/project/Projects.scala @@ -14,10 +14,13 @@ object Projects extends Build { lazy val kamonCore = Project("kamon-core", file("kamon-core")) + .dependsOn(kamonMacros) .settings(basicSettings: _*) .settings(formatSettings: _*) .settings(aspectJSettings: _*) .settings( + mappings in (Compile, packageBin) ++= mappings.in(kamonMacros, Compile, packageBin).value, + mappings in (Compile, packageSrc) ++= mappings.in(kamonMacros, Compile, packageSrc).value, libraryDependencies ++= compile(akkaActor, aspectJ, aspectjWeaver, hdrHistogram, jsr166) ++ provided(logback) ++ @@ -81,13 +84,18 @@ object Projects extends Build { .settings(formatSettings: _*) .settings(libraryDependencies ++= compile(akkaActor) ++ test(scalatest, akkaTestKit, slf4Api, slf4nop)) .dependsOn(kamonCore) - + lazy val kamonDatadog = Project("kamon-datadog", file("kamon-datadog")) .settings(basicSettings: _*) .settings(formatSettings: _*) .settings(libraryDependencies ++= compile(akkaActor) ++ test(scalatest, akkaTestKit, slf4Api, slf4nop)) .dependsOn(kamonCore) + lazy val kamonMacros = Project("kamon-macros", file("kamon-macros")) + .settings(basicSettings: _*) + .settings(formatSettings: _*) + .settings(noPublishing: _*) + .settings(libraryDependencies ++= compile(scalaCompiler)) val noPublishing = Seq(publish := (), publishLocal := (), publishArtifact := false) } diff --git a/project/Settings.scala b/project/Settings.scala index f2ccc32c..6cee3133 100644 --- a/project/Settings.scala +++ b/project/Settings.scala @@ -8,8 +8,10 @@ import scalariform.formatter.preferences._ object Settings { - lazy val basicSettings = seq( - scalaVersion := "2.10.3", + val ScalaVersion = "2.10.3" + + lazy val basicSettings = Seq( + scalaVersion := ScalaVersion, resolvers ++= Dependencies.resolutionRepos, fork in run := true, scalacOptions := Seq( |