From 2d56fbe91c051cf722addf91b40a9e3f906bb5ec Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Thu, 17 Apr 2014 16:38:48 +0200 Subject: [PATCH] build #15021,#13755 Emit statsd events during build --- .../docs/persistence/PersistenceDocSpec.scala | 8 +- project/AkkaBuild.scala | 11 +- project/TestExtras.scala | 129 ++++++++++++++++++ project/plugins.sbt | 2 + 4 files changed, 141 insertions(+), 9 deletions(-) create mode 100644 project/TestExtras.scala diff --git a/akka-docs/rst/scala/code/docs/persistence/PersistenceDocSpec.scala b/akka-docs/rst/scala/code/docs/persistence/PersistenceDocSpec.scala index 6253446802..1292d2a517 100644 --- a/akka-docs/rst/scala/code/docs/persistence/PersistenceDocSpec.scala +++ b/akka-docs/rst/scala/code/docs/persistence/PersistenceDocSpec.scala @@ -84,16 +84,16 @@ trait PersistenceDocSpec { } //#deletion } - + class MyProcessor4 extends Processor { //#recovery-completed override def preStart(): Unit = { super.preStart() self ! "FIRST" } - + def receive = initializing.orElse(active) - + def initializing: Receive = { case "FIRST" => recoveryCompleted() @@ -102,7 +102,7 @@ trait PersistenceDocSpec { case other if recoveryFinished => stash() } - + def recoveryCompleted(): Unit = { // perform init after recovery, before any other messages // ... diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index d541f97509..730e807949 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -25,6 +25,7 @@ import java.nio.charset.Charset import java.util.Properties import annotation.tailrec import Unidoc.{ JavaDoc, javadocSettings, junidocSources, sunidoc, unidocExclude } +import TestExtras. { JUnitFileReporting, StatsDMetrics } import com.typesafe.sbt.S3Plugin.{ S3, s3Settings } object AkkaBuild extends Build { @@ -50,6 +51,7 @@ object AkkaBuild extends Build { base = file("."), settings = parentSettings ++ Release.settings ++ Unidoc.settings ++ Publish.versionSettings ++ SphinxSupport.settings ++ Dist.settings ++ s3Settings ++ mimaSettings ++ unidocScaladocSettings ++ + StatsDMetrics.settings ++ Protobuf.settings ++ inConfig(JavaDoc)(Defaults.configSettings) ++ Seq( parallelExecution in GlobalScope := System.getProperty("akka.parallelExecution", "false").toBoolean, Publish.defaultPublishTo in ThisBuild <<= crossTarget / "repository", @@ -148,11 +150,9 @@ object AkkaBuild extends Build { id = "akka-actor-tests", base = file("akka-actor-tests"), dependencies = Seq(testkit % "compile;test->test"), - settings = defaultSettings ++ formatSettings ++ scaladocSettings ++ Seq( + settings = defaultSettings ++ formatSettings ++ scaladocSettings ++ Seq( publishArtifact in Compile := false, - libraryDependencies ++= Dependencies.actorTests, - testOptions += Tests.Argument(TestFrameworks.JUnit, "-v", "-a"), - reportBinaryIssues := () // disable bin comp check + libraryDependencies ++= Dependencies.actorTests ) ) @@ -723,7 +723,8 @@ object AkkaBuild extends Build { // add reportBinaryIssues to validatePullRequest on minor version maintenance branch validatePullRequest <<= validatePullRequest.dependsOn(reportBinaryIssues) - ) ++ mavenLocalResolverSettings + ) ++ mavenLocalResolverSettings ++ JUnitFileReporting.settings ++ StatsDMetrics.settings + val validatePullRequest = TaskKey[Unit]("validate-pull-request", "Additional tasks for pull request validation") // the tasks that to run for validation is defined in defaultSettings diff --git a/project/TestExtras.scala b/project/TestExtras.scala new file mode 100644 index 0000000000..8fe483d094 --- /dev/null +++ b/project/TestExtras.scala @@ -0,0 +1,129 @@ +package akka + +import sbt._ +import Keys._ + +import com.timgroup.statsd.{StatsDClientErrorHandler, NonBlockingStatsDClient, StatsDClient} +import java.util.concurrent.{ConcurrentHashMap, ConcurrentLinkedQueue} +import sbt.testing.{TestSelector, Status, Event} +import java.io.File +import com.typesafe.tools.mima.plugin.MimaKeys._ +import scala.Some +import scala.Some +import java.io.File +import sbt.File +import scala.util.Try + +object TestExtras { + + object JUnitFileReporting { + val settings = Seq( + // we can enable junit-style reports everywhere with this + testOptions += Tests.Argument(TestFrameworks.JUnit, "-v", "-a", "-u", (target.value / "test-reports").getAbsolutePath), + testOptions += Tests.Argument(TestFrameworks.ScalaTest, "-u", (target.value / "test-reports").getAbsolutePath) + ) + } + + object StatsDMetrics { + + val statsd = config("statsd") extend Test + + val enabled = settingKey[Boolean]("Set to true when you want to send stats to statsd; Enable with `-Dakka.sbt.statsd=true`") + + val prefix = settingKey[String]("Prefix given to all metrics sent to statsd") + + val host = settingKey[String]("Host where statsd is located (ip, or hostname)") + + val port = settingKey[Int]("Port on which statsd is listening, defaults to 8125") + + + + val settings = Seq( + // configuration + enabled in statsd := sys.props("akka.sbt.statsd") == "true", + prefix in statsd := Option(sys.props("akka.sbt.statsd.prefix")).getOrElse("akka_master"), + host in statsd := Option(sys.props("akka.sbt.statsd.host")).getOrElse("54.72.154.120"), + port in statsd := Option(sys.props("akka.sbt.statsd.port")).flatMap(p => Try(p.toInt).toOption).getOrElse(8125), + + testListeners in(Test, test) ++= { + // for test + enabled.in(statsd).value match { + case true => Seq(StatsDTestListener(streams.value.log, prefix.in(statsd).value, host.in(statsd).value, port.in(statsd).value)) + case _ => Nil + } + }, + testListeners ++= { + // for testOnly + enabled.in(statsd).value match { + case true => Seq(StatsDTestListener(streams.value.log, prefix.in(statsd).value, host.in(statsd).value, port.in(statsd).value)) + case _ => Nil + } + } + ) + + case class StatsDTestListener(log: Logger, prefix: String, host: String, port: Int) extends TestsListener { + + var client: NonBlockingStatsDClient = _ + + override def doInit(): Unit = { + log.info(s"Initialised StatsDTestsListener (sending stats to $host:$port)") + client = new NonBlockingStatsDClient(prefix, host, port, new StatsDClientErrorHandler { + override def handle(exception: Exception): Unit = log.error(exception.toString) + }) + } + + override def testEvent(event: TestEvent) { + event.detail foreach { + det => + det.status match { + case Status.Success => + client.incrementCounter(testCounterKey(det, det.status)) + client.recordExecutionTime(testTimerKey(det), det.duration.toInt + 1000 + (Math.random() * 1000).toInt) + + case status => + client.incrementCounter(testCounterKey(det, status)) + } + } + } + + override def endGroup(name: String, result: TestResult.Value) { + // manual switch instead of toStringing class name all the time + result match { + case TestResult.Passed => client.incrementCounter(keySuccess(name)) + case TestResult.Failed => client.incrementCounter(keyFail(name)) + case TestResult.Error => client.incrementCounter(keyError(name)) + } + } + + override def endGroup(name: String, t: Throwable) { + client.incrementCounter(keyError(name)) + } + + override def startGroup(name: String) { + // do nothing + } + + override def doComplete(finalResult: TestResult.Value): Unit = { + log.debug("Final test run result: " + finalResult) + log.info("Shutting down StatsDTestsListener client...") + if (client != null) + client.stop() + } + + private def testTimerKey(det: Event): String = s"${det.fullyQualifiedName}.${testSelectorToId(det.selector)}" + + private def testSelectorToId(sel: testing.Selector): String = sel.asInstanceOf[TestSelector].testName().replaceAll("[. ']", "_") + + private def testCounterKey(det: Event, status: Status): String = s"${det.fullyQualifiedName}.${status.toString.toLowerCase}" + + private def keySuccess(fullyQualifiedName: String): String = fullyQualifiedName + ".success" + + private def keyFail(fullyQualifiedName: String): String = fullyQualifiedName + ".fail" + + private def keyError(fullyQualifiedName: String): String = fullyQualifiedName + ".error" + + } + + } + +} \ No newline at end of file diff --git a/project/plugins.sbt b/project/plugins.sbt index 99dd090fec..34f1c11b7a 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -17,3 +17,5 @@ addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.1.6") addSbtPlugin("com.typesafe.sbt" % "sbt-pgp" % "0.8.1") addSbtPlugin("com.typesafe.sbt" % "sbt-s3" % "0.5") + +libraryDependencies += "com.timgroup" % "java-statsd-client" % "2.0.0"