From c55de15854f0bcd2248f2398b384716eb1d6585e Mon Sep 17 00:00:00 2001 From: Jairam Chandar Date: Tue, 4 Apr 2017 17:42:23 +0100 Subject: [PATCH] Add play-2.5 example This is a slightly modified version of https://github.com/kamon-io/Kamon/tree/master/kamon-examples/kamon-play-example-2.4.x Currently there aren't any examples for using Kamon in a Play! 2.5.x project. `kamon-play` now is a Play module rather than a Plugin, the existing examples in https://github.com/kamon-io/Kamon/tree/master/kamon-examples are obsolete. Also, `play.api.mvc.Filter` now requires an implicit `Materializer`, so `TraceLocalFilter` in the existing examples can no longer be an object. --- .../app/Filters.scala | 6 ++ .../app/controllers/KamonPlayExample.scala | 82 +++++++++++++++++++ .../app/filters/TraceLocalFilter.scala | 64 +++++++++++++++ .../kamon-play-example-2.5.x/build.sbt | 17 ++++ .../conf/application.conf | 63 ++++++++++++++ .../kamon-play-example-2.5.x/conf/logback.xml | 27 ++++++ .../kamon-play-example-2.5.x/conf/routes | 5 ++ .../project/build.properties | 1 + .../project/plugins.sbt | 7 ++ 9 files changed, 272 insertions(+) create mode 100644 kamon-examples/kamon-play-example-2.5.x/app/Filters.scala create mode 100644 kamon-examples/kamon-play-example-2.5.x/app/controllers/KamonPlayExample.scala create mode 100644 kamon-examples/kamon-play-example-2.5.x/app/filters/TraceLocalFilter.scala create mode 100644 kamon-examples/kamon-play-example-2.5.x/build.sbt create mode 100644 kamon-examples/kamon-play-example-2.5.x/conf/application.conf create mode 100644 kamon-examples/kamon-play-example-2.5.x/conf/logback.xml create mode 100644 kamon-examples/kamon-play-example-2.5.x/conf/routes create mode 100644 kamon-examples/kamon-play-example-2.5.x/project/build.properties create mode 100644 kamon-examples/kamon-play-example-2.5.x/project/plugins.sbt diff --git a/kamon-examples/kamon-play-example-2.5.x/app/Filters.scala b/kamon-examples/kamon-play-example-2.5.x/app/Filters.scala new file mode 100644 index 000000000..51025c12e --- /dev/null +++ b/kamon-examples/kamon-play-example-2.5.x/app/Filters.scala @@ -0,0 +1,6 @@ +import javax.inject.Inject + +import play.api.http.DefaultHttpFilters +import filters.TraceLocalFilter + +class Filters @Inject() (traceLocalFilter: TraceLocalFilter) extends DefaultHttpFilters(traceLocalFilter) \ No newline at end of file diff --git a/kamon-examples/kamon-play-example-2.5.x/app/controllers/KamonPlayExample.scala b/kamon-examples/kamon-play-example-2.5.x/app/controllers/KamonPlayExample.scala new file mode 100644 index 000000000..720e4516d --- /dev/null +++ b/kamon-examples/kamon-play-example-2.5.x/app/controllers/KamonPlayExample.scala @@ -0,0 +1,82 @@ +/* =================================================== + * Copyright © 2013-2015 the kamon project + * + * 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 filters.{TraceLocalContainer, TraceLocalKey} +import kamon.play.action.TraceName +import kamon.Kamon +import kamon.trace.TraceLocal +import play.api.Logger +import play.api.libs.concurrent.Execution.Implicits.defaultContext +import play.api.mvc.{Action, Controller} + +import scala.concurrent._ + +/** + * Run the following commands from console: + * + * aspectj-runner:run + * + * and finally testing: + * + * 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 + **/ +class KamonPlayExample extends Controller { + + val logger = Logger(this.getClass) + val counter = Kamon.metrics.counter("my-counter") + + 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 with trace name") + Ok("Say hello to Kamon with trace name") + } + } + } + + def incrementCounter = Action.async { + Future { + logger.info("increment") + counter.increment() + Ok("increment") + } + } + + def updateTraceLocal() = Action.async { + Future { + TraceLocal.store(TraceLocalKey)(TraceLocalContainer("MyTraceToken","MyImportantHeader")) + logger.info("storeInTraceLocal") + Ok("storeInTraceLocal") + } + } +} diff --git a/kamon-examples/kamon-play-example-2.5.x/app/filters/TraceLocalFilter.scala b/kamon-examples/kamon-play-example-2.5.x/app/filters/TraceLocalFilter.scala new file mode 100644 index 000000000..8716dcdba --- /dev/null +++ b/kamon-examples/kamon-play-example-2.5.x/app/filters/TraceLocalFilter.scala @@ -0,0 +1,64 @@ +/* =================================================== + * Copyright © 2013-2014 the kamon project + * + * 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 javax.inject.Inject + +import akka.stream.Materializer +import kamon.trace.TraceLocal +import kamon.trace.TraceLocal.{AvailableToMdc, TraceLocalKey} +import play.api.Logger +import play.api.mvc.{Filter, RequestHeader, Result} +import play.api.libs.concurrent.Execution.Implicits.defaultContext + +import scala.concurrent.Future + +case class TraceLocalContainer(traceToken:String, importantHeader:String) + +object TraceLocalKey extends TraceLocalKey[TraceLocalContainer] + +/* + By default kamon spreads the trace-token-header-name, but sometimes is necessary pass through the application requests with some information like + extra headers, with kamon it's possible using the 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 + */ +class TraceLocalFilter @Inject() (implicit val mat: Materializer) extends Filter { + val logger = Logger(this.getClass) + val TraceLocalStorageKey = "MyTraceLocalStorageKey" + + val userAgentHeader = "User-Agent" + + //this value will be available in the MDC at the moment to call to Logger.*()s + val UserAgentHeaderAvailableToMDC = AvailableToMdc(userAgentHeader) + + override def apply(next: (RequestHeader) ⇒ Future[Result])(header: RequestHeader): Future[Result] = { + + def onResult(result:Result) = { + val traceLocalContainer = TraceLocal.retrieve(TraceLocalKey).getOrElse(TraceLocalContainer("unknown","unknown")) + result.withHeaders(TraceLocalStorageKey -> traceLocalContainer.traceToken) + } + + //update the TraceLocalStorage + TraceLocal.store(TraceLocalKey)(TraceLocalContainer(header.headers.get(TraceLocalStorageKey).getOrElse("unknown"), "unknown")) + TraceLocal.store(UserAgentHeaderAvailableToMDC)(header.headers.get(userAgentHeader).getOrElse("unknown")) + + //call the action + next(header).map(onResult) + } +} diff --git a/kamon-examples/kamon-play-example-2.5.x/build.sbt b/kamon-examples/kamon-play-example-2.5.x/build.sbt new file mode 100644 index 000000000..f85f4d5cf --- /dev/null +++ b/kamon-examples/kamon-play-example-2.5.x/build.sbt @@ -0,0 +1,17 @@ +name := "kamon-play-example-2.5.x" + +version := "1.0-SNAPSHOT" + +scalaVersion := "2.11.8" + +val kamonVersion = "0.6.6" + +libraryDependencies ++= Seq( + "io.kamon" %% "kamon-play-2.5" % kamonVersion, + "io.kamon" %% "kamon-system-metrics" % kamonVersion, + "io.kamon" %% "kamon-statsd" % kamonVersion, + "io.kamon" %% "kamon-log-reporter" % kamonVersion, + "org.aspectj" % "aspectjweaver" % "1.8.9" +) + +enablePlugins(PlayScala) \ No newline at end of file diff --git a/kamon-examples/kamon-play-example-2.5.x/conf/application.conf b/kamon-examples/kamon-play-example-2.5.x/conf/application.conf new file mode 100644 index 000000000..9a4abd66b --- /dev/null +++ b/kamon-examples/kamon-play-example-2.5.x/conf/application.conf @@ -0,0 +1,63 @@ +#kamon related configuration + +kamon { + + metric { + tick-interval = 1 second + } + + 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 = "activator-akka-kamon-statsd" + } + } + + play { + include-trace-token-header = true + trace-token-header-name = "X-Trace-Token" + } + + modules { + kamon-statsd.auto-start = yes + kamon-log-reporter.auto-start = no + } +} + +# 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`AZE9EOphrmf;;6JsAN" + +# The application languages +# ~~~~~ +play.i18n.langs = ["en"] + +# HttpRequestHandler +play.http.requestHandler = "play.http.DefaultHttpRequestHandler" diff --git a/kamon-examples/kamon-play-example-2.5.x/conf/logback.xml b/kamon-examples/kamon-play-example-2.5.x/conf/logback.xml new file mode 100644 index 000000000..a4a10bd05 --- /dev/null +++ b/kamon-examples/kamon-play-example-2.5.x/conf/logback.xml @@ -0,0 +1,27 @@ + + + + + + + + %date{HH:mm:ss.SSS} %-5level [%traceToken][%X{User-Agent}] [%thread] %logger{55} - %msg%n + + + + + + + + + + + + + + + diff --git a/kamon-examples/kamon-play-example-2.5.x/conf/routes b/kamon-examples/kamon-play-example-2.5.x/conf/routes new file mode 100644 index 000000000..ecc6cd914 --- /dev/null +++ b/kamon-examples/kamon-play-example-2.5.x/conf/routes @@ -0,0 +1,5 @@ +# Routes +GET /helloKamon controllers.KamonPlayExample.sayHello +GET /helloKamonWithTraceName controllers.KamonPlayExample.sayHelloWithTraceName +GET /incrementCounter controllers.KamonPlayExample.incrementCounter +GET /updateTraceLocal controllers.KamonPlayExample.updateTraceLocal \ No newline at end of file diff --git a/kamon-examples/kamon-play-example-2.5.x/project/build.properties b/kamon-examples/kamon-play-example-2.5.x/project/build.properties new file mode 100644 index 000000000..e51b4bed2 --- /dev/null +++ b/kamon-examples/kamon-play-example-2.5.x/project/build.properties @@ -0,0 +1 @@ +sbt.version-0.13.13 diff --git a/kamon-examples/kamon-play-example-2.5.x/project/plugins.sbt b/kamon-examples/kamon-play-example-2.5.x/project/plugins.sbt new file mode 100644 index 000000000..139df2961 --- /dev/null +++ b/kamon-examples/kamon-play-example-2.5.x/project/plugins.sbt @@ -0,0 +1,7 @@ +// Comment to get more information during initialization +logLevel := Level.Warn + +// Play plugin +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.10") + +addSbtPlugin("io.kamon" % "aspectj-play-runner" % "0.1.4")