Skip to content

Commit

Permalink
implement servlet + default registry
Browse files Browse the repository at this point in the history
  • Loading branch information
liorhar committed Jun 19, 2013
1 parent 7850338 commit d2591aa
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1,3 +1,4 @@
target/
.idea/
.idea_modules/
logs/
13 changes: 11 additions & 2 deletions build.sbt
@@ -1,13 +1,22 @@
organization:= "com.kenshoo"

name := "play-metrics"

version := "0.1.0-SNAPSHOT"

scalaVersion := "2.10.0"

resolvers += "Typesafe Releases" at "http://repo.typesafe.com/typesafe/releases/"

testOptions in Test += Tests.Argument("junitxml", "console")

libraryDependencies ++= Seq(
"com.codahale.metrics" % "metrics-core" % "3.0.0",
"com.codahale.metrics" % "metrics-json" % "3.0.0",
"com.codahale.metrics" % "metrics-jvm" % "3.0.0",
// "play" % "play" % "2.1.0" % "provided",
"org.specs2" % "specs2_2.10" % "1.13" % "test"
"play" %% "play" % "2.1.0" % "provided",
//test
"play" %% "play-test" % "2.1.0" % "test",
"org.specs2" % "specs2_2.10" % "1.13" % "test",
"org.mockito" % "mockito-all" % "1.9.5" % "test"
)
48 changes: 48 additions & 0 deletions src/main/scala/com/kenshoo/play/metrics/MertricsPlugin.scala
@@ -0,0 +1,48 @@
package com.kenshoo.play.metrics

import com.codahale.metrics.jvm.{ThreadStatesGaugeSet, GarbageCollectorMetricSet, MemoryUsageGaugeSet}
import com.codahale.metrics.{MetricRegistry, SharedMetricRegistries}
import play.api.{Plugin, Application}
import com.fasterxml.jackson.databind.ObjectMapper
import com.codahale.metrics.json.MetricsModule
import java.util.concurrent.TimeUnit


object MetricsRegistry {
def default(implicit app : Application) = {
app.plugin[MetricsPlugin] match {
case Some(plugin) => SharedMetricRegistries.getOrCreate(plugin.registryName)
case None => throw new Exception("metrics plugin is not found")
}
}
}


class MetricsPlugin(val app: Application) extends Plugin {
val validUnits = Some(Set("NANOSECONDS", "MICROSECONDS", "MILLISECONDS", "SECONDS", "MINUTES", "HOURS", "DAYS"))

lazy val registryName = app.configuration.getString("metrics.name").getOrElse("default")
lazy val mapper: ObjectMapper = new ObjectMapper()
lazy val rateUnit = app.configuration.getString("metrics.rateUnit", validUnits).getOrElse("SECONDS")
lazy val durationUnit = app.configuration.getString("metrics.durationUnit", validUnits).getOrElse("SECONDS")
lazy val showSamples = app.configuration.getBoolean("metrics.showSamples").getOrElse(true)

implicit def stringToTimeUnit(s: String) : TimeUnit = TimeUnit.valueOf(s)

override def onStart() {
if (enabled) {
val registry: MetricRegistry = SharedMetricRegistries.getOrCreate(registryName)
val jvmMetricsEnabled = app.configuration.getBoolean("metrics.jvm").getOrElse(true)
if (jvmMetricsEnabled) {
registry.registerAll(new GarbageCollectorMetricSet())
registry.registerAll(new MemoryUsageGaugeSet())
registry.registerAll(new ThreadStatesGaugeSet())
}
val module = new MetricsModule(rateUnit, durationUnit, showSamples)
mapper.registerModule(module)
}
}

override def enabled = app.configuration.getBoolean("metrics.enabled").getOrElse(true)
}

41 changes: 41 additions & 0 deletions src/main/scala/com/kenshoo/play/metrics/MetricsController.scala
@@ -0,0 +1,41 @@
package com.kenshoo.play.metrics

import play.api.mvc.{Action, Controller}
import com.codahale.metrics.MetricRegistry
import com.fasterxml.jackson.databind.{ObjectWriter, ObjectMapper}
import java.io.StringWriter
import play.api.Play.current
import play.api.Application



trait MetricsController {
self: Controller =>

val registry: MetricRegistry

val app: Application

def serialize(mapper: ObjectMapper) = {
val writer: ObjectWriter = mapper.writerWithDefaultPrettyPrinter()
val stringWriter = new StringWriter()
writer.writeValue(stringWriter, registry)
Ok(stringWriter.toString).as("application/json").withHeaders("Cache-Control" -> "must-revalidate,no-cache,no-store")
}

def metrics = Action {
app.plugin[MetricsPlugin] match {
case Some(plugin) => plugin.enabled match {
case true => serialize(plugin.mapper)
case false => InternalServerError("metrics plugin not enabled")
}
case None => InternalServerError("metrics plugin is not found")
}
}

}

object MetricsController extends Controller with MetricsController {
lazy val registry = MetricsRegistry.default
lazy val app = current
}
1 change: 0 additions & 1 deletion src/main/scala/hw.scala

This file was deleted.

@@ -0,0 +1,40 @@
package com.kenshoo.play.metrics

import org.specs2.mutable.{Before, Specification}
import play.api.mvc.{Controller, Result}
import play.api.test.{WithApplication, FakeApplication, FakeRequest}
import play.api.libs.json.{Json, JsValue}
import play.api.test.Helpers._
import com.codahale.metrics.MetricRegistry

class MetricsControllerSpec extends Specification {
"metrics servlet" should {
"returns json result" in new ControllerRegistry {
val result: Result = controller.metrics(FakeRequest())
val jsValue: JsValue = Json.parse(contentAsString(result))
(jsValue \ "counters" \ "my-counter" \ "count").as[Int] mustEqual(1)
}

"sets no cache control" in new ControllerRegistry {
val result: Result = controller.metrics(FakeRequest())
headers(result) must haveValue("must-revalidate,no-cache,no-store")
}

}

abstract class ControllerRegistry extends WithApplication(
FakeApplication(additionalPlugins = Seq("com.kenshoo.play.metrics.MetricsPlugin"))
) with Before {
lazy val testRegistry = new MetricRegistry

def before {
testRegistry.counter("my-counter").inc()
}

lazy val controller = new Controller() with MetricsController {
override val registry = testRegistry
override val app = implicitApp
}
}

}
70 changes: 70 additions & 0 deletions src/test/scala/com/kenshoo/play/metrics/MetricsPluginTest.scala
@@ -0,0 +1,70 @@
package com.kenshoo.play.metrics

import org.specs2.mutable.Specification
import org.specs2.mock.Mockito
import play.api.{Configuration, Application}
import com.codahale.metrics.{Metric, Meter, SharedMetricRegistries}
import org.specs2.specification.BeforeAfterExample
import scala.collection.JavaConversions._
import scala.collection.mutable.Map


class MetricsPluginSpec extends Specification with Mockito with BeforeAfterExample{
sequential
"metrics plugin" should {

"be enabled by default" in {
val plugin = config()
plugin.enabled must beTrue
}

"can be turned off" in {
val plugin = config(Option(false))
plugin.enabled must beFalse
}

"can be turned on" in {
val plugin = config(Option(true))
plugin.enabled must beTrue
}

"registers default metric registry" in {
val plugin = config()
plugin.onStart()
SharedMetricRegistries.names().contains("default") must beTrue
}

"registers metric by name" in {
val plugin = config(name = Option("name"))
plugin.onStart()
SharedMetricRegistries.names().contains("name") must beTrue
}

"registers jvm metrics" in {
val plugin = config()
plugin.onStart()
val metrics: Map[String, Metric] = SharedMetricRegistries.getOrCreate("default").getMetrics
metrics must haveKey("heap.usage")
}
}

def config(enabled: Option[Boolean] = Option.empty,
name: Option[String] = Option.empty,
jvm: Option[Boolean] = Option.empty): MetricsPlugin = {
val app = mock[Application]
val config = mock[Configuration]
app.configuration returns config
config.getString(anyString, any[Option[Set[String]]]) returns Option.empty
config.getBoolean("metrics.enabled") returns enabled
config.getString("metrics.name") returns name
config.getBoolean("metrics.jvm") returns jvm
config.getBoolean("metrics.showSamples") returns Option.empty
new MetricsPlugin(app)
}

def after {
SharedMetricRegistries.clear()
}

protected def before: Any = {}
}

0 comments on commit d2591aa

Please sign in to comment.