diff options
author | Ivan Topolnjak <ivantopo@gmail.com> | 2015-02-12 11:30:06 +0100 |
---|---|---|
committer | Ivan Topolnjak <ivantopo@gmail.com> | 2015-02-13 05:15:30 +0100 |
commit | c6bb65535bcc3cc1ff3834a91473ee8dfa6145e8 (patch) | |
tree | d7dbe6a1007b168998f167ac74a98744542c6fa8 | |
parent | 6729c9632245328a007332cdcce7d362584d735a (diff) | |
download | Kamon-c6bb65535bcc3cc1ff3834a91473ee8dfa6145e8.tar.gz Kamon-c6bb65535bcc3cc1ff3834a91473ee8dfa6145e8.tar.bz2 Kamon-c6bb65535bcc3cc1ff3834a91473ee8dfa6145e8.zip |
! all: Kamon now works as a single instance in a companion object.
56 files changed, 674 insertions, 493 deletions
diff --git a/kamon-akka-remote/src/main/scala/kamon/akka/instrumentation/RemotingInstrumentation.scala b/kamon-akka-remote/src/main/scala/kamon/akka/instrumentation/RemotingInstrumentation.scala index eb18ed87..32a3bcc9 100644 --- a/kamon-akka-remote/src/main/scala/kamon/akka/instrumentation/RemotingInstrumentation.scala +++ b/kamon-akka-remote/src/main/scala/kamon/akka/instrumentation/RemotingInstrumentation.scala @@ -5,7 +5,8 @@ import akka.remote.instrumentation.TraceContextAwareWireFormats.{ TraceContextAw import akka.remote.{ RemoteActorRefProvider, Ack, SeqNo } import akka.remote.WireFormats._ import akka.util.ByteString -import kamon.trace.{ Tracer, TraceContext } +import kamon.Kamon +import kamon.trace.TraceContext import kamon.util.MilliTimestamp import org.aspectj.lang.ProceedingJoinPoint import org.aspectj.lang.annotation._ @@ -83,7 +84,7 @@ class RemotingInstrumentation { if (ackAndEnvelope.hasEnvelope && ackAndEnvelope.getEnvelope.hasTraceContext) { val remoteTraceContext = ackAndEnvelope.getEnvelope.getTraceContext val system = provider.guardian.underlying.system - val tracer = Tracer.get(system) + val tracer = Kamon.tracer val ctx = tracer.newContext( remoteTraceContext.getTraceName, diff --git a/kamon-akka-remote/src/test/scala/kamon/akka/instrumentation/RemotingInstrumentationSpec.scala b/kamon-akka-remote/src/test/scala/kamon/akka/instrumentation/RemotingInstrumentationSpec.scala index 367a7349..ccfde35a 100644 --- a/kamon-akka-remote/src/test/scala/kamon/akka/instrumentation/RemotingInstrumentationSpec.scala +++ b/kamon-akka-remote/src/test/scala/kamon/akka/instrumentation/RemotingInstrumentationSpec.scala @@ -16,23 +16,27 @@ import scala.concurrent.duration._ import scala.util.control.NonFatal class RemotingInstrumentationSpec extends TestKitBase with WordSpecLike with Matchers with ImplicitSender { - implicit lazy val system: ActorSystem = ActorSystem("remoting-spec-local-system", ConfigFactory.parseString( - """ - |akka { - | loggers = ["akka.event.slf4j.Slf4jLogger"] - | - | actor { - | provider = "akka.remote.RemoteActorRefProvider" - | } - | remote { - | enabled-transports = ["akka.remote.netty.tcp"] - | netty.tcp { - | hostname = "127.0.0.1" - | port = 2552 - | } - | } - |} - """.stripMargin)) + + implicit lazy val system: ActorSystem = { + Kamon.start() + ActorSystem("remoting-spec-local-system", ConfigFactory.parseString( + """ + |akka { + | loggers = ["akka.event.slf4j.Slf4jLogger"] + | + | actor { + | provider = "akka.remote.RemoteActorRefProvider" + | } + | remote { + | enabled-transports = ["akka.remote.netty.tcp"] + | netty.tcp { + | hostname = "127.0.0.1" + | port = 2552 + | } + | } + |} + """.stripMargin)) + } val remoteSystem: ActorSystem = ActorSystem("remoting-spec-remote-system", ConfigFactory.parseString( """ @@ -52,13 +56,12 @@ class RemotingInstrumentationSpec extends TestKitBase with WordSpecLike with Mat |} """.stripMargin)) - lazy val kamon = Kamon(system) val RemoteSystemAddress = AddressFromURIString("akka.tcp://remoting-spec-remote-system@127.0.0.1:2553") - import kamon.tracer.newContext + import Kamon.tracer "The Remoting instrumentation" should { "propagate the TraceContext when creating a new remote actor" in { - TraceContext.withContext(newContext("deploy-remote-actor", "deploy-remote-actor-1")) { + TraceContext.withContext(tracer.newContext("deploy-remote-actor", "deploy-remote-actor-1")) { system.actorOf(TraceTokenReplier.remoteProps(Some(testActor), RemoteSystemAddress), "remote-deploy-fixture") } @@ -68,7 +71,7 @@ class RemotingInstrumentationSpec extends TestKitBase with WordSpecLike with Mat "propagate the TraceContext when sending a message to a remotely deployed actor" in { val remoteRef = system.actorOf(TraceTokenReplier.remoteProps(None, RemoteSystemAddress), "remote-message-fixture") - TraceContext.withContext(newContext("message-remote-actor", "message-remote-actor-1")) { + TraceContext.withContext(tracer.newContext("message-remote-actor", "message-remote-actor-1")) { remoteRef ! "reply-trace-token" } @@ -80,7 +83,7 @@ class RemotingInstrumentationSpec extends TestKitBase with WordSpecLike with Mat implicit val askTimeout = Timeout(10 seconds) val remoteRef = system.actorOf(TraceTokenReplier.remoteProps(None, RemoteSystemAddress), "remote-ask-and-pipe-fixture") - TraceContext.withContext(newContext("ask-and-pipe-remote-actor", "ask-and-pipe-remote-actor-1")) { + TraceContext.withContext(tracer.newContext("ask-and-pipe-remote-actor", "ask-and-pipe-remote-actor-1")) { (remoteRef ? "reply-trace-token") pipeTo (testActor) } @@ -92,7 +95,7 @@ class RemotingInstrumentationSpec extends TestKitBase with WordSpecLike with Mat remoteSystem.actorOf(TraceTokenReplier.props(None), "actor-selection-target-b") val selection = system.actorSelection(RemoteSystemAddress + "/user/actor-selection-target-*") - TraceContext.withContext(newContext("message-remote-actor-selection", "message-remote-actor-selection-1")) { + TraceContext.withContext(tracer.newContext("message-remote-actor-selection", "message-remote-actor-selection-1")) { selection ! "reply-trace-token" } @@ -104,7 +107,7 @@ class RemotingInstrumentationSpec extends TestKitBase with WordSpecLike with Mat "propagate the TraceContext a remotely supervised child fails" in { val supervisor = system.actorOf(Props(new SupervisorOfRemote(testActor, RemoteSystemAddress))) - TraceContext.withContext(newContext("remote-supervision", "remote-supervision-1")) { + TraceContext.withContext(tracer.newContext("remote-supervision", "remote-supervision-1")) { supervisor ! "fail" } @@ -115,7 +118,7 @@ class RemotingInstrumentationSpec extends TestKitBase with WordSpecLike with Mat remoteSystem.actorOf(TraceTokenReplier.props(None), "remote-routee") val router = system.actorOf(RoundRobinGroup(List(RemoteSystemAddress + "/user/actor-selection-target-*")).props(), "router") - TraceContext.withContext(newContext("remote-routee", "remote-routee-1")) { + TraceContext.withContext(tracer.newContext("remote-routee", "remote-routee-1")) { router ! "reply-trace-token" } diff --git a/kamon-akka/src/main/scala/kamon/akka/instrumentation/ActorCellInstrumentation.scala b/kamon-akka/src/main/scala/kamon/akka/instrumentation/ActorCellInstrumentation.scala index c961737d..7c722569 100644 --- a/kamon-akka/src/main/scala/kamon/akka/instrumentation/ActorCellInstrumentation.scala +++ b/kamon-akka/src/main/scala/kamon/akka/instrumentation/ActorCellInstrumentation.scala @@ -19,8 +19,9 @@ package akka.kamon.instrumentation import akka.actor._ import akka.dispatch.{ Envelope, MessageDispatcher } import akka.routing.RoutedActorCell +import kamon.Kamon import kamon.akka.{ RouterMetrics, ActorMetrics } -import kamon.metric.{ Metrics, Entity } +import kamon.metric.Entity import kamon.trace._ import org.aspectj.lang.ProceedingJoinPoint import org.aspectj.lang.annotation._ @@ -33,7 +34,7 @@ class ActorCellInstrumentation { @After("actorCellCreation(cell, system, ref, props, dispatcher, parent)") def afterCreation(cell: ActorCell, system: ActorSystem, ref: ActorRef, props: Props, dispatcher: MessageDispatcher, parent: ActorRef): Unit = { - Metrics.get(system).register(ActorMetrics, ref.path.elements.mkString("/")).map { registration ⇒ + Kamon.metrics.register(ActorMetrics, ref.path.elements.mkString("/")).map { registration ⇒ val cellMetrics = cell.asInstanceOf[ActorCellMetrics] cellMetrics.entity = registration.entity @@ -89,14 +90,14 @@ class ActorCellInstrumentation { def afterStop(cell: ActorCell): Unit = { val cellMetrics = cell.asInstanceOf[ActorCellMetrics] cellMetrics.recorder.map { _ ⇒ - Metrics.get(cell.system).unregister(cellMetrics.entity) + Kamon.metrics.unregister(cellMetrics.entity) } // The Stop can't be captured from the RoutedActorCell so we need to put this piece of cleanup here. if (cell.isInstanceOf[RoutedActorCell]) { val routedCellMetrics = cell.asInstanceOf[RoutedActorCellMetrics] routedCellMetrics.routerRecorder.map { _ ⇒ - Metrics.get(cell.system).unregister(routedCellMetrics.routerEntity) + Kamon.metrics.unregister(routedCellMetrics.routerEntity) } } } @@ -123,7 +124,7 @@ class RoutedActorCellInstrumentation { @After("routedActorCellCreation(cell, system, ref, props, dispatcher, routeeProps, supervisor)") def afterRoutedActorCellCreation(cell: RoutedActorCell, system: ActorSystem, ref: ActorRef, props: Props, dispatcher: MessageDispatcher, routeeProps: Props, supervisor: ActorRef): Unit = { - Metrics.get(system).register(RouterMetrics, ref.path.elements.mkString("/")).map { registration ⇒ + Kamon.metrics.register(RouterMetrics, ref.path.elements.mkString("/")).map { registration ⇒ val cellMetrics = cell.asInstanceOf[RoutedActorCellMetrics] cellMetrics.routerEntity = registration.entity diff --git a/kamon-akka/src/main/scala/kamon/akka/instrumentation/AskPatternInstrumentation.scala b/kamon-akka/src/main/scala/kamon/akka/instrumentation/AskPatternInstrumentation.scala index 28bfcae9..e1dcdf32 100644 --- a/kamon-akka/src/main/scala/kamon/akka/instrumentation/AskPatternInstrumentation.scala +++ b/kamon-akka/src/main/scala/kamon/akka/instrumentation/AskPatternInstrumentation.scala @@ -44,7 +44,7 @@ class AskPatternInstrumentation { actor match { // the AskPattern will only work for InternalActorRef's with these conditions. case ref: InternalActorRef if !ref.isTerminated && timeout.duration.length > 0 ⇒ - val akkaExtension = ctx.lookupExtension(Akka) + val akkaExtension = Kamon.extension(Akka) val future = pjp.proceed().asInstanceOf[Future[AnyRef]] val system = ref.provider.guardian.underlying.system diff --git a/kamon-akka/src/main/scala/kamon/akka/instrumentation/DispatcherInstrumentation.scala b/kamon-akka/src/main/scala/kamon/akka/instrumentation/DispatcherInstrumentation.scala index f4bc31c4..7b15c443 100644 --- a/kamon-akka/src/main/scala/kamon/akka/instrumentation/DispatcherInstrumentation.scala +++ b/kamon-akka/src/main/scala/kamon/akka/instrumentation/DispatcherInstrumentation.scala @@ -22,8 +22,9 @@ import akka.actor.{ ActorSystem, ActorSystemImpl } import akka.dispatch.ForkJoinExecutorConfigurator.AkkaForkJoinPool import akka.dispatch._ import akka.kamon.instrumentation.LookupDataAware.LookupData +import kamon.Kamon import kamon.akka.{ AkkaDispatcherMetrics, ThreadPoolExecutorDispatcherMetrics, ForkJoinPoolDispatcherMetrics } -import kamon.metric.{ Metrics, Entity } +import kamon.metric.Entity import org.aspectj.lang.ProceedingJoinPoint import org.aspectj.lang.annotation._ @@ -58,10 +59,10 @@ class DispatcherInstrumentation { private def registerDispatcher(dispatcherName: String, executorService: ExecutorService, system: ActorSystem): Unit = executorService match { case fjp: AkkaForkJoinPool ⇒ - Metrics.get(system).register(ForkJoinPoolDispatcherMetrics.factory(fjp), dispatcherName) + Kamon.metrics.register(ForkJoinPoolDispatcherMetrics.factory(fjp), dispatcherName) case tpe: ThreadPoolExecutor ⇒ - Metrics.get(system).register(ThreadPoolExecutorDispatcherMetrics.factory(tpe), dispatcherName) + Kamon.metrics.register(ThreadPoolExecutorDispatcherMetrics.factory(tpe), dispatcherName) case others ⇒ // Currently not interested in other kinds of dispatchers. } @@ -119,7 +120,7 @@ class DispatcherInstrumentation { import lazyExecutor.lookupData if (lookupData.actorSystem != null) - Metrics.get(lookupData.actorSystem).unregister(Entity(lookupData.dispatcherName, AkkaDispatcherMetrics.Category)) + Kamon.metrics.unregister(Entity(lookupData.dispatcherName, AkkaDispatcherMetrics.Category)) } } diff --git a/kamon-akka/src/test/scala/kamon/metric/ActorMetricsSpec.scala b/kamon-akka/src/test/scala/kamon/akka/ActorMetricsSpec.scala index 322abed2..19a71053 100644 --- a/kamon-akka/src/test/scala/kamon/metric/ActorMetricsSpec.scala +++ b/kamon-akka/src/test/scala/kamon/akka/ActorMetricsSpec.scala @@ -13,42 +13,44 @@ * ========================================================================================= */ -package kamon.metric +package kamon.akka import java.nio.LongBuffer -import kamon.Kamon -import kamon.akka.ActorMetrics -import kamon.metric.ActorMetricsTestActor._ -import kamon.metric.instrument.CollectionContext -import org.scalatest.{ BeforeAndAfterAll, WordSpecLike, Matchers } -import akka.testkit.{ ImplicitSender, TestProbe, TestKitBase } import akka.actor._ +import akka.testkit.TestProbe import com.typesafe.config.ConfigFactory +import kamon.Kamon +import kamon.akka.ActorMetricsTestActor._ +import kamon.metric.EntitySnapshot +import kamon.metric.instrument.CollectionContext +import kamon.testkit.BaseKamonSpec + import scala.concurrent.duration._ -class ActorMetricsSpec extends TestKitBase with WordSpecLike with Matchers with ImplicitSender with BeforeAndAfterAll { - implicit lazy val system: ActorSystem = ActorSystem("actor-metrics-spec", ConfigFactory.parseString( - """ - |kamon.metric { - | tick-interval = 1 hour - | default-collection-context-buffer-size = 10 - | - | filters { - | akka-actor { - | includes = [ "user/tracked-*", "user/measuring-*", "user/clean-after-collect", "user/stop" ] - | excludes = [ "user/tracked-explicitly-excluded", "user/non-tracked-actor" ] - | } - | } - | - | instrument-settings { - | akka-actor.mailbox-size.refresh-interval = 1 hour - | } - |} - | - |akka.loglevel = OFF - | - """.stripMargin)) +class ActorMetricsSpec extends BaseKamonSpec("actor-metrics-spec") { + override lazy val config = + ConfigFactory.parseString( + """ + |kamon.metric { + | tick-interval = 1 hour + | default-collection-context-buffer-size = 10 + | + | filters { + | akka-actor { + | includes = [ "user/tracked-*", "user/measuring-*", "user/clean-after-collect", "user/stop" ] + | excludes = [ "user/tracked-explicitly-excluded", "user/non-tracked-actor" ] + | } + | } + | + | instrument-settings { + | akka-actor.mailbox-size.refresh-interval = 1 hour + | } + |} + | + |akka.loglevel = OFF + | + """.stripMargin) "the Kamon actor metrics" should { "respect the configured include and exclude filters" in new ActorMetricsFixtures { @@ -163,7 +165,7 @@ class ActorMetricsSpec extends TestKitBase with WordSpecLike with Matchers with def actorRecorderName(ref: ActorRef): String = ref.path.elements.mkString("/") def actorMetricsRecorderOf(ref: ActorRef): Option[ActorMetrics] = - Kamon(Metrics)(system).register(ActorMetrics, actorRecorderName(ref)).map(_.recorder) + Kamon.metrics.register(ActorMetrics, actorRecorderName(ref)).map(_.recorder) def collectMetricsOf(ref: ActorRef): Option[EntitySnapshot] = { Thread.sleep(5) // Just in case the test advances a bit faster than the actor being tested. diff --git a/kamon-akka/src/test/scala/kamon/metric/DispatcherMetricsSpec.scala b/kamon-akka/src/test/scala/kamon/akka/DispatcherMetricsSpec.scala index 2c530da9..209b5cf5 100644 --- a/kamon-akka/src/test/scala/kamon/metric/DispatcherMetricsSpec.scala +++ b/kamon-akka/src/test/scala/kamon/akka/DispatcherMetricsSpec.scala @@ -13,72 +13,73 @@ * ========================================================================================= */ -package kamon.metric +package kamon.akka import java.nio.LongBuffer -import akka.actor.{ PoisonPill, Props, ActorRef, ActorSystem } +import akka.actor.{ ActorRef, ActorSystem } import akka.dispatch.MessageDispatcher import akka.testkit.{ TestKitBase, TestProbe } import com.typesafe.config.ConfigFactory import kamon.Kamon -import kamon.akka.{ ForkJoinPoolDispatcherMetrics, ThreadPoolExecutorDispatcherMetrics } -import kamon.metric.ActorMetricsTestActor.{ Pong, Ping } import kamon.metric.instrument.CollectionContext +import kamon.metric.{ EntityRecorder, EntitySnapshot } +import kamon.testkit.BaseKamonSpec import org.scalatest.{ BeforeAndAfterAll, Matchers, WordSpecLike } -import scala.concurrent.{ Await, Future } import scala.concurrent.duration._ +import scala.concurrent.{ Await, Future } -class DispatcherMetricsSpec extends TestKitBase with WordSpecLike with Matchers with BeforeAndAfterAll { - implicit lazy val system: ActorSystem = ActorSystem("dispatcher-metrics-spec", ConfigFactory.parseString( - """ - |kamon.metric { - | tick-interval = 1 hour - | default-collection-context-buffer-size = 10 - | - | filters = { - | akka-dispatcher { - | includes = [ "*" ] - | excludes = [ "explicitly-excluded" ] - | } - | } - | - | default-instrument-settings { - | gauge.refresh-interval = 1 hour - | min-max-counter.refresh-interval = 1 hour - | } - |} - | - |explicitly-excluded { - | type = "Dispatcher" - | executor = "fork-join-executor" - |} - | - |tracked-fjp { - | type = "Dispatcher" - | executor = "fork-join-executor" - | - | fork-join-executor { - | parallelism-min = 8 - | parallelism-factor = 100.0 - | parallelism-max = 22 - | } - |} - | - |tracked-tpe { - | type = "Dispatcher" - | executor = "thread-pool-executor" - | - | thread-pool-executor { - | core-pool-size-min = 7 - | core-pool-size-factor = 100.0 - | max-pool-size-factor = 100.0 - | max-pool-size-max = 21 - | } - |} - | - """.stripMargin)) +class DispatcherMetricsSpec extends BaseKamonSpec("dispatcher-metrics-spec") { + override lazy val config = + ConfigFactory.parseString( + """ + |kamon.metric { + | tick-interval = 1 hour + | default-collection-context-buffer-size = 10 + | + | filters = { + | akka-dispatcher { + | includes = [ "*" ] + | excludes = [ "explicitly-excluded" ] + | } + | } + | + | default-instrument-settings { + | gauge.refresh-interval = 1 hour + | min-max-counter.refresh-interval = 1 hour + | } + |} + | + |explicitly-excluded { + | type = "Dispatcher" + | executor = "fork-join-executor" + |} + | + |tracked-fjp { + | type = "Dispatcher" + | executor = "fork-join-executor" + | + | fork-join-executor { + | parallelism-min = 8 + | parallelism-factor = 100.0 + | parallelism-max = 22 + | } + |} + | + |tracked-tpe { + | type = "Dispatcher" + | executor = "thread-pool-executor" + | + | thread-pool-executor { + | core-pool-size-min = 7 + | core-pool-size-factor = 100.0 + | max-pool-size-factor = 100.0 + | max-pool-size-max = 21 + | } + |} + | + """.stripMargin) "the Kamon dispatcher metrics" should { "respect the configured include and exclude filters" in { @@ -95,6 +96,7 @@ class DispatcherMetricsSpec extends TestKitBase with WordSpecLike with Matchers "record metrics for a dispatcher with thread-pool-executor" in { implicit val tpeDispatcher = system.dispatchers.lookup("tracked-tpe") + refreshDispatcherInstruments(tpeDispatcher) collectDispatcherMetrics(tpeDispatcher) Await.result({ @@ -156,14 +158,10 @@ class DispatcherMetricsSpec extends TestKitBase with WordSpecLike with Matchers } - val collectionContext = new CollectionContext { - val buffer: LongBuffer = LongBuffer.allocate(10000) - } - def actorRecorderName(ref: ActorRef): String = ref.path.elements.mkString("/") def findDispatcherRecorder(dispatcher: MessageDispatcher): Option[EntityRecorder] = - Kamon(Metrics)(system).find(dispatcher.id, "akka-dispatcher") + Kamon.metrics.find(dispatcher.id, "akka-dispatcher") def collectDispatcherMetrics(dispatcher: MessageDispatcher): EntitySnapshot = findDispatcherRecorder(dispatcher).map(_.collect(collectionContext)).get diff --git a/kamon-akka/src/test/scala/kamon/metric/RouterMetricsSpec.scala b/kamon-akka/src/test/scala/kamon/akka/RouterMetricsSpec.scala index 5f6bbb4f..ec55648b 100644 --- a/kamon-akka/src/test/scala/kamon/metric/RouterMetricsSpec.scala +++ b/kamon-akka/src/test/scala/kamon/akka/RouterMetricsSpec.scala @@ -13,40 +13,41 @@ * ========================================================================================= */ -package kamon.metric +package kamon.akka import java.nio.LongBuffer import akka.actor._ import akka.routing._ -import akka.testkit.{ ImplicitSender, TestKitBase, TestProbe } +import akka.testkit.TestProbe import com.typesafe.config.ConfigFactory import kamon.Kamon -import kamon.akka.RouterMetrics -import kamon.metric.RouterMetricsTestActor._ +import kamon.akka.RouterMetricsTestActor._ +import kamon.metric.EntitySnapshot import kamon.metric.instrument.CollectionContext -import org.scalatest.{ BeforeAndAfterAll, Matchers, WordSpecLike } +import kamon.testkit.BaseKamonSpec import scala.concurrent.duration._ -class RouterMetricsSpec extends TestKitBase with WordSpecLike with Matchers with ImplicitSender with BeforeAndAfterAll { - implicit lazy val system: ActorSystem = ActorSystem("router-metrics-spec", ConfigFactory.parseString( - """ - |kamon.metric { - | tick-interval = 1 hour - | default-collection-context-buffer-size = 10 - | - | filters = { - | akka-router { - | includes = [ "user/tracked-*", "user/measuring-*", "user/stop-*" ] - | excludes = [ "user/tracked-explicitly-excluded-*"] - | } - | } - |} - | - |akka.loglevel = OFF - | - """.stripMargin)) +class RouterMetricsSpec extends BaseKamonSpec("router-metrics-spec") { + override lazy val config = + ConfigFactory.parseString( + """ + |kamon.metric { + | tick-interval = 1 hour + | default-collection-context-buffer-size = 10 + | + | filters = { + | akka-router { + | includes = [ "user/tracked-*", "user/measuring-*", "user/stop-*" ] + | excludes = [ "user/tracked-explicitly-excluded-*"] + | } + | } + |} + | + |akka.loglevel = OFF + | + """.stripMargin) "the Kamon router metrics" should { "respect the configured include and exclude filters" in new RouterMetricsFixtures { @@ -204,7 +205,7 @@ class RouterMetricsSpec extends TestKitBase with WordSpecLike with Matchers with } def routerMetricsRecorderOf(routerName: String): Option[RouterMetrics] = - Kamon(Metrics)(system).register(RouterMetrics, routerName).map(_.recorder) + Kamon.metrics.register(RouterMetrics, routerName).map(_.recorder) def collectMetricsOf(routerName: String): Option[EntitySnapshot] = { Thread.sleep(5) // Just in case the test advances a bit faster than the actor being tested. diff --git a/kamon-akka/src/test/scala/kamon/akka/instrumentation/AskPatternInstrumentationSpec.scala b/kamon-akka/src/test/scala/kamon/akka/instrumentation/AskPatternInstrumentationSpec.scala index 0d63a19e..44b90642 100644 --- a/kamon-akka/src/test/scala/kamon/akka/instrumentation/AskPatternInstrumentationSpec.scala +++ b/kamon-akka/src/test/scala/kamon/akka/instrumentation/AskPatternInstrumentationSpec.scala @@ -114,7 +114,7 @@ class AskPatternInstrumentationSpec extends BaseKamonSpec("ask-pattern-tracing-s } def setAskPatternTimeoutWarningMode(mode: String): Unit = { - val target = Kamon(Akka)(system) + val target = Kamon(Akka) val field = target.getClass.getDeclaredField("askPatternTimeoutWarning") field.setAccessible(true) field.set(target, mode) diff --git a/kamon-core/src/main/scala/kamon/Kamon.scala b/kamon-core/src/main/scala/kamon/Kamon.scala index 6fedc065..2bed4737 100644 --- a/kamon-core/src/main/scala/kamon/Kamon.scala +++ b/kamon-core/src/main/scala/kamon/Kamon.scala @@ -16,49 +16,61 @@ package kamon import _root_.akka.actor import _root_.akka.actor._ -import com.typesafe.config.Config +import com.typesafe.config.{ ConfigFactory, Config } import kamon.metric._ -import kamon.supervisor.ModuleSupervisor -import kamon.trace.{ Tracer, TracerExtension } +import kamon.trace.{ TracerExtensionImpl, TracerExtension } -class Kamon(val actorSystem: ActorSystem) { - val metrics: MetricsExtension = Metrics.get(actorSystem) - val tracer: TracerExtension = Tracer.get(actorSystem) - val userMetrics: UserMetricsExtension = UserMetrics.get(actorSystem) +object Kamon { + trait Extension extends actor.Extension - // This will cause all auto-start modules to initiate. - ModuleSupervisor.get(actorSystem) + private case class KamonCoreComponents( + metrics: MetricsExtension, + tracer: TracerExtension, + userMetrics: UserMetricsExtension) - def shutdown(): Unit = - actorSystem.shutdown() -} + @volatile private var _system: ActorSystem = _ + @volatile private var _coreComponents: Option[KamonCoreComponents] = None -object Kamon { - trait Extension extends actor.Extension - def apply[T <: Extension](key: ExtensionId[T])(implicit system: ActorSystem): T = key(system) + def start(config: Config): Unit = synchronized { + if (_coreComponents.isEmpty) { + val metrics = MetricsExtensionImpl(config) + val simpleMetrics = UserMetricsExtensionImpl(metrics) + val tracer = TracerExtensionImpl(metrics, config) - def apply(): Kamon = - apply("kamon") + _coreComponents = Some(KamonCoreComponents(metrics, tracer, simpleMetrics)) + _system = ActorSystem("kamon", config) - def apply(actorSystemName: String): Kamon = - apply(ActorSystem(actorSystemName)) + metrics.start(_system) + tracer.start(_system) - def apply(actorSystemName: String, config: Config): Kamon = - apply(ActorSystem(actorSystemName, config)) + } else sys.error("Kamon has already been started.") + } - def apply(system: ActorSystem): Kamon = - new Kamon(system) + def start(): Unit = + start(ConfigFactory.load) - def create(): Kamon = - apply() + def metrics: MetricsExtension = + ifStarted(_.metrics) - def create(actorSystemName: String): Kamon = - apply(ActorSystem(actorSystemName)) + def tracer: TracerExtension = + ifStarted(_.tracer) - def create(actorSystemName: String, config: Config): Kamon = - apply(ActorSystem(actorSystemName, config)) + def userMetrics: UserMetricsExtension = + ifStarted(_.userMetrics) - def create(system: ActorSystem): Kamon = - new Kamon(system) + def apply[T <: Kamon.Extension](key: ExtensionId[T]): T = + ifStarted { _ ⇒ + if (_system ne null) + key(_system) + else + sys.error("Cannot retrieve extensions while Kamon is being initialized.") + } + + def extension[T <: Kamon.Extension](key: ExtensionId[T]): T = + apply(key) + + private def ifStarted[T](thunk: KamonCoreComponents ⇒ T): T = + _coreComponents.map(thunk(_)) getOrElse (sys.error("Kamon has not been started yet.")) + +} -}
\ No newline at end of file diff --git a/kamon-core/src/main/scala/kamon/metric/MetricsExtension.scala b/kamon-core/src/main/scala/kamon/metric/MetricsExtension.scala index 88352e21..87911352 100644 --- a/kamon-core/src/main/scala/kamon/metric/MetricsExtension.scala +++ b/kamon-core/src/main/scala/kamon/metric/MetricsExtension.scala @@ -16,25 +16,17 @@ package kamon.metric -import akka.actor +import com.typesafe.config.Config import kamon.metric.SubscriptionsDispatcher.{ Unsubscribe, Subscribe } -import kamon.Kamon -import kamon.metric.instrument.{ InstrumentFactory, CollectionContext } -import kamon.supervisor.ModuleSupervisor +import kamon.metric.instrument.{ DefaultRefreshScheduler, InstrumentFactory, CollectionContext } import scala.collection.concurrent.TrieMap import akka.actor._ -import kamon.util.{ FastDispatch, TriemapAtomicGetOrElseUpdate } - -object Metrics extends ExtensionId[MetricsExtension] with ExtensionIdProvider { - override def get(system: ActorSystem): MetricsExtension = super.get(system) - def lookup(): ExtensionId[_ <: actor.Extension] = Metrics - def createExtension(system: ExtendedActorSystem): MetricsExtension = new MetricsExtensionImpl(system) -} +import kamon.util.{ LazyActorRef, TriemapAtomicGetOrElseUpdate } case class EntityRegistration[T <: EntityRecorder](entity: Entity, recorder: T) -trait MetricsExtension extends Kamon.Extension { +trait MetricsExtension { def settings: MetricsExtensionSettings def shouldTrack(entity: Entity): Boolean def shouldTrack(entityName: String, category: String): Boolean = @@ -63,16 +55,11 @@ trait MetricsExtension extends Kamon.Extension { def instrumentFactory(category: String): InstrumentFactory } -class MetricsExtensionImpl(system: ExtendedActorSystem) extends MetricsExtension { - import FastDispatch.Syntax - - val settings = MetricsExtensionSettings(system) - +private[kamon] class MetricsExtensionImpl(config: Config) extends MetricsExtension { private val _trackedEntities = TrieMap.empty[Entity, EntityRecorder] - private val _collectionContext = buildDefaultCollectionContext - private val _metricsCollectionDispatcher = system.dispatchers.lookup(settings.metricCollectionDispatcher) - private lazy val _subscriptions = ModuleSupervisor.get(system).createModule("subscriptions-dispatcher", - SubscriptionsDispatcher.props(settings.tickInterval, collectSnapshots).withDispatcher(settings.metricCollectionDispatcher)) + private val _subscriptions = new LazyActorRef + + val settings = MetricsExtensionSettings(config) def shouldTrack(entity: Entity): Boolean = settings.entityFilters.get(entity.category).map { @@ -110,10 +97,10 @@ class MetricsExtensionImpl(system: ExtendedActorSystem) extends MetricsExtension find(Entity(name, category)) def subscribe(filter: SubscriptionFilter, subscriber: ActorRef, permanent: Boolean): Unit = - _subscriptions.fastDispatch(Subscribe(filter, subscriber, permanent))(_metricsCollectionDispatcher) + _subscriptions.tell(Subscribe(filter, subscriber, permanent)) def unsubscribe(subscriber: ActorRef): Unit = - _subscriptions.fastDispatch(Unsubscribe(subscriber))(_metricsCollectionDispatcher) + _subscriptions.tell(Unsubscribe(subscriber)) def buildDefaultCollectionContext: CollectionContext = CollectionContext(settings.defaultCollectionContextBufferSize) @@ -121,16 +108,34 @@ class MetricsExtensionImpl(system: ExtendedActorSystem) extends MetricsExtension def instrumentFactory(category: String): InstrumentFactory = settings.instrumentFactories.getOrElse(category, settings.defaultInstrumentFactory) - /** - * Collect and dispatch. - */ - private def collectSnapshots(): Map[Entity, EntitySnapshot] = { + private[kamon] def collectSnapshots(collectionContext: CollectionContext): Map[Entity, EntitySnapshot] = { val builder = Map.newBuilder[Entity, EntitySnapshot] _trackedEntities.foreach { - case (identity, recorder) ⇒ builder += ((identity, recorder.collect(_collectionContext))) + case (identity, recorder) ⇒ builder += ((identity, recorder.collect(collectionContext))) } builder.result() } + + /** + * Metrics Extension initialization. + */ + private var _system: ActorSystem = null + private lazy val _start = { + _subscriptions.point(_system.actorOf(SubscriptionsDispatcher.props(settings.tickInterval, this), "metrics")) + settings.pointScheduler(DefaultRefreshScheduler(_system.scheduler, _system.dispatcher)) + } + + def start(system: ActorSystem): Unit = synchronized { + _system = system + _start + _system = null + } +} + +private[kamon] object MetricsExtensionImpl { + + def apply(config: Config) = + new MetricsExtensionImpl(config) } diff --git a/kamon-core/src/main/scala/kamon/metric/MetricsExtensionSettings.scala b/kamon-core/src/main/scala/kamon/metric/MetricsExtensionSettings.scala index 84624336..9881ed00 100644 --- a/kamon-core/src/main/scala/kamon/metric/MetricsExtensionSettings.scala +++ b/kamon-core/src/main/scala/kamon/metric/MetricsExtensionSettings.scala @@ -16,9 +16,8 @@ package kamon.metric -import akka.actor.ExtendedActorSystem import com.typesafe.config.Config -import kamon.metric.instrument.{ RefreshScheduler, InstrumentFactory, DefaultInstrumentSettings, InstrumentCustomSettings } +import kamon.metric.instrument._ import kamon.util.GlobPathFilter import scala.concurrent.duration.FiniteDuration @@ -27,15 +26,19 @@ import scala.concurrent.duration.FiniteDuration * Configuration settings for the Metrics extension, as read from the `kamon.metric` configuration key. */ case class MetricsExtensionSettings( - tickInterval: FiniteDuration, - defaultCollectionContextBufferSize: Int, - trackUnmatchedEntities: Boolean, - entityFilters: Map[String, EntityFilter], - instrumentFactories: Map[String, InstrumentFactory], - defaultInstrumentFactory: InstrumentFactory, - metricCollectionDispatcher: String, - refreshSchedulerDispatcher: String, - refreshScheduler: RefreshScheduler) + tickInterval: FiniteDuration, + defaultCollectionContextBufferSize: Int, + trackUnmatchedEntities: Boolean, + entityFilters: Map[String, EntityFilter], + instrumentFactories: Map[String, InstrumentFactory], + defaultInstrumentFactory: InstrumentFactory, + refreshScheduler: RefreshScheduler) { + + private[kamon] def pointScheduler(targetScheduler: RefreshScheduler): Unit = refreshScheduler match { + case lrs: LazyRefreshScheduler ⇒ lrs.point(targetScheduler) + case others ⇒ + } +} /** * @@ -49,23 +52,21 @@ object MetricsExtensionSettings { import kamon.util.ConfigTools.Syntax import scala.concurrent.duration._ - def apply(system: ExtendedActorSystem): MetricsExtensionSettings = { - val metricConfig = system.settings.config.getConfig("kamon.metric") + def apply(config: Config): MetricsExtensionSettings = { + val metricConfig = config.getConfig("kamon.metric") val tickInterval = metricConfig.getFiniteDuration("tick-interval") val collectBufferSize = metricConfig.getInt("default-collection-context-buffer-size") val trackUnmatchedEntities = metricConfig.getBoolean("track-unmatched-entities") val entityFilters = loadFilters(metricConfig.getConfig("filters")) val defaultInstrumentSettings = DefaultInstrumentSettings.fromConfig(metricConfig.getConfig("default-instrument-settings")) - val metricCollectionDispatcher = metricConfig.getString("dispatchers.metric-collection") - val refreshSchedulerDispatcher = metricConfig.getString("dispatchers.refresh-scheduler") - val refreshScheduler = RefreshScheduler(system.scheduler, system.dispatchers.lookup(refreshSchedulerDispatcher)) + val refreshScheduler = new LazyRefreshScheduler val instrumentFactories = loadInstrumentFactories(metricConfig.getConfig("instrument-settings"), defaultInstrumentSettings, refreshScheduler) val defaultInstrumentFactory = new InstrumentFactory(Map.empty, defaultInstrumentSettings, refreshScheduler) MetricsExtensionSettings(tickInterval, collectBufferSize, trackUnmatchedEntities, entityFilters, instrumentFactories, - defaultInstrumentFactory, metricCollectionDispatcher, refreshSchedulerDispatcher, refreshScheduler) + defaultInstrumentFactory, refreshScheduler) } /** diff --git a/kamon-core/src/main/scala/kamon/metric/SubscriptionsDispatcher.scala b/kamon-core/src/main/scala/kamon/metric/SubscriptionsDispatcher.scala index f616be35..68b545a5 100644 --- a/kamon-core/src/main/scala/kamon/metric/SubscriptionsDispatcher.scala +++ b/kamon-core/src/main/scala/kamon/metric/SubscriptionsDispatcher.scala @@ -24,11 +24,12 @@ import scala.concurrent.duration.FiniteDuration /** * Manages subscriptions to metrics and dispatch snapshots on every tick to all subscribers. */ -private[kamon] class SubscriptionsDispatcher(interval: FiniteDuration, collector: () ⇒ Map[Entity, EntitySnapshot]) extends Actor { +private[kamon] class SubscriptionsDispatcher(interval: FiniteDuration, metricsExtension: MetricsExtensionImpl) extends Actor { var lastTick = MilliTimestamp.now var oneShotSubscriptions = Map.empty[ActorRef, SubscriptionFilter] var permanentSubscriptions = Map.empty[ActorRef, SubscriptionFilter] val tickSchedule = context.system.scheduler.schedule(interval, interval, self, Tick)(context.dispatcher) + val collectionContext = metricsExtension.buildDefaultCollectionContext def receive = { case Tick ⇒ processTick() @@ -38,7 +39,7 @@ private[kamon] class SubscriptionsDispatcher(interval: FiniteDuration, collector } def processTick(): Unit = - dispatch(collector()) + dispatch(metricsExtension.collectSnapshots(collectionContext)) def subscribe(filter: SubscriptionFilter, subscriber: ActorRef, permanent: Boolean): Unit = { def addSubscription(storage: Map[ActorRef, SubscriptionFilter]): Map[ActorRef, SubscriptionFilter] = @@ -80,8 +81,8 @@ private[kamon] class SubscriptionsDispatcher(interval: FiniteDuration, collector } object SubscriptionsDispatcher { - def props(interval: FiniteDuration, collector: () ⇒ Map[Entity, EntitySnapshot]): Props = - Props(new SubscriptionsDispatcher(interval, collector)) + def props(interval: FiniteDuration, metricsExtension: MetricsExtensionImpl): Props = + Props(new SubscriptionsDispatcher(interval, metricsExtension)) case object Tick case class Unsubscribe(subscriber: ActorRef) diff --git a/kamon-core/src/main/scala/kamon/metric/TickMetricSnapshotBuffer.scala b/kamon-core/src/main/scala/kamon/metric/TickMetricSnapshotBuffer.scala index 358dade8..dfc5d5f0 100644 --- a/kamon-core/src/main/scala/kamon/metric/TickMetricSnapshotBuffer.scala +++ b/kamon-core/src/main/scala/kamon/metric/TickMetricSnapshotBuffer.scala @@ -29,7 +29,7 @@ class TickMetricSnapshotBuffer(flushInterval: FiniteDuration, receiver: ActorRef import MapMerge.Syntax val flushSchedule = context.system.scheduler.schedule(flushInterval, flushInterval, self, FlushBuffer)(context.dispatcher) - val collectionContext: CollectionContext = Kamon(Metrics)(context.system).buildDefaultCollectionContext + val collectionContext: CollectionContext = Kamon.metrics.buildDefaultCollectionContext def receive = empty diff --git a/kamon-core/src/main/scala/kamon/metric/UserMetrics.scala b/kamon-core/src/main/scala/kamon/metric/UserMetrics.scala index e6ec5e99..e0818292 100644 --- a/kamon-core/src/main/scala/kamon/metric/UserMetrics.scala +++ b/kamon-core/src/main/scala/kamon/metric/UserMetrics.scala @@ -16,30 +16,13 @@ package kamon.metric -import akka.actor -import akka.actor.{ ActorSystem, ExtendedActorSystem, ExtensionIdProvider, ExtensionId } -import kamon.Kamon import kamon.metric.instrument.Gauge.CurrentValueCollector import kamon.metric.instrument.Histogram.DynamicRange import kamon.metric.instrument._ import scala.concurrent.duration.FiniteDuration -object UserMetrics extends ExtensionId[UserMetricsExtension] with ExtensionIdProvider { - override def get(system: ActorSystem): UserMetricsExtension = super.get(system) - def lookup(): ExtensionId[_ <: actor.Extension] = UserMetrics - def createExtension(system: ExtendedActorSystem): UserMetricsExtension = { - val metricsExtension = Metrics.get(system) - val instrumentFactory = metricsExtension.instrumentFactory(entity.category) - val userMetricsExtension = new UserMetricsExtensionImpl(instrumentFactory) - - metricsExtension.register(entity, userMetricsExtension).recorder - } - - val entity = Entity("user-metric", "user-metric") -} - -trait UserMetricsExtension extends Kamon.Extension { +trait UserMetricsExtension { def histogram(name: String): Histogram def histogram(name: String, dynamicRange: DynamicRange): Histogram def histogram(name: String, unitOfMeasurement: UnitOfMeasurement): Histogram @@ -86,7 +69,7 @@ trait UserMetricsExtension extends Kamon.Extension { } -class UserMetricsExtensionImpl(instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) with UserMetricsExtension { +private[kamon] class UserMetricsExtensionImpl(instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) with UserMetricsExtension { override def histogram(name: String): Histogram = super.histogram(name) @@ -206,4 +189,16 @@ class UserMetricsExtensionImpl(instrumentFactory: InstrumentFactory) extends Gen override def removeCounter(key: CounterKey): Unit = super.removeCounter(key) +} + +private[kamon] object UserMetricsExtensionImpl { + val UserMetricEntity = Entity("user-metric", "user-metric") + + def apply(metricsExtension: MetricsExtension): UserMetricsExtensionImpl = { + val instrumentFactory = metricsExtension.instrumentFactory(UserMetricEntity.category) + val userMetricsExtension = new UserMetricsExtensionImpl(instrumentFactory) + + metricsExtension.register(UserMetricEntity, userMetricsExtension).recorder + } + }
\ No newline at end of file diff --git a/kamon-core/src/main/scala/kamon/metric/instrument/Instrument.scala b/kamon-core/src/main/scala/kamon/metric/instrument/Instrument.scala index 68d5c876..59b4b443 100644 --- a/kamon-core/src/main/scala/kamon/metric/instrument/Instrument.scala +++ b/kamon-core/src/main/scala/kamon/metric/instrument/Instrument.scala @@ -51,22 +51,3 @@ object CollectionContext { } } -trait RefreshScheduler { - def schedule(interval: FiniteDuration, refresh: () ⇒ Unit): Cancellable -} - -object RefreshScheduler { - val NoopScheduler = new RefreshScheduler { - def schedule(interval: FiniteDuration, refresh: () ⇒ Unit): Cancellable = new Cancellable { - override def isCancelled: Boolean = true - override def cancel(): Boolean = true - } - } - - def apply(scheduler: Scheduler, dispatcher: MessageDispatcher): RefreshScheduler = new RefreshScheduler { - def schedule(interval: FiniteDuration, refresh: () ⇒ Unit): Cancellable = - scheduler.schedule(interval, interval)(refresh.apply())(dispatcher) - } - - def create(scheduler: Scheduler, dispatcher: MessageDispatcher): RefreshScheduler = apply(scheduler, dispatcher) -}
\ No newline at end of file diff --git a/kamon-core/src/main/scala/kamon/metric/instrument/RefreshScheduler.scala b/kamon-core/src/main/scala/kamon/metric/instrument/RefreshScheduler.scala new file mode 100644 index 00000000..adb08713 --- /dev/null +++ b/kamon-core/src/main/scala/kamon/metric/instrument/RefreshScheduler.scala @@ -0,0 +1,99 @@ +package kamon.metric.instrument + +import akka.actor.{ Scheduler, Cancellable } +import org.HdrHistogram.WriterReaderPhaser + +import scala.collection.concurrent.TrieMap +import scala.concurrent.ExecutionContext +import scala.concurrent.duration.FiniteDuration + +trait RefreshScheduler { + def schedule(interval: FiniteDuration, refresh: () ⇒ Unit): Cancellable +} + +/** + * Default implementation of RefreshScheduler that simply uses an [[akka.actor.Scheduler]] to schedule tasks to be run + * in the provided ExecutionContext. + */ +class DefaultRefreshScheduler(scheduler: Scheduler, dispatcher: ExecutionContext) extends RefreshScheduler { + def schedule(interval: FiniteDuration, refresh: () ⇒ Unit): Cancellable = + scheduler.schedule(interval, interval)(refresh.apply())(dispatcher) +} + +object DefaultRefreshScheduler { + def apply(scheduler: Scheduler, dispatcher: ExecutionContext): RefreshScheduler = + new DefaultRefreshScheduler(scheduler, dispatcher) + + def create(scheduler: Scheduler, dispatcher: ExecutionContext): RefreshScheduler = + apply(scheduler, dispatcher) +} + +/** + * RefreshScheduler implementation that accumulates all the scheduled actions until it is pointed to another refresh + * scheduler. Once it is pointed, all subsequent calls to `schedule` will immediately be scheduled in the pointed + * scheduler. + */ +class LazyRefreshScheduler extends RefreshScheduler { + private val _schedulerPhaser = new WriterReaderPhaser + private val _backlog = new TrieMap[(FiniteDuration, () ⇒ Unit), RepointableCancellable]() + @volatile private var _target: Option[RefreshScheduler] = None + + def schedule(interval: FiniteDuration, refresh: () ⇒ Unit): Cancellable = { + val criticalEnter = _schedulerPhaser.writerCriticalSectionEnter() + try { + _target.map { scheduler ⇒ + scheduler.schedule(interval, refresh) + + } getOrElse { + val entry = (interval, refresh) + val cancellable = new RepointableCancellable(entry) + + _backlog.put(entry, cancellable) + cancellable + } + + } finally { + _schedulerPhaser.writerCriticalSectionExit(criticalEnter) + } + } + + def point(target: RefreshScheduler): Unit = try { + _schedulerPhaser.readerLock() + + if (_target.isEmpty) { + _target = Some(target) + _schedulerPhaser.flipPhase(10000L) + _backlog.dropWhile { + case ((interval, refresh), repointableCancellable) ⇒ + repointableCancellable.point(target.schedule(interval, refresh)) + true + } + } else sys.error("A LazyRefreshScheduler cannot be pointed more than once.") + } finally { _schedulerPhaser.readerUnlock() } + + class RepointableCancellable(entry: (FiniteDuration, () ⇒ Unit)) extends Cancellable { + private var _isCancelled = false + private var _cancellable: Option[Cancellable] = None + + def isCancelled: Boolean = synchronized { + _cancellable.map(_.isCancelled).getOrElse(_isCancelled) + } + + def cancel(): Boolean = synchronized { + _isCancelled = true + _cancellable.map(_.cancel()).getOrElse(_backlog.remove(entry).nonEmpty) + } + + def point(cancellable: Cancellable): Unit = synchronized { + if (_cancellable.isEmpty) { + _cancellable = Some(cancellable) + + if (_isCancelled) + cancellable.cancel() + + } else sys.error("A RepointableCancellable cannot be pointed more than once.") + + } + } +} + diff --git a/kamon-core/src/main/scala/kamon/trace/MetricsOnlyContext.scala b/kamon-core/src/main/scala/kamon/trace/MetricsOnlyContext.scala index e62178dd..5f7fdff5 100644 --- a/kamon-core/src/main/scala/kamon/trace/MetricsOnlyContext.scala +++ b/kamon-core/src/main/scala/kamon/trace/MetricsOnlyContext.scala @@ -18,16 +18,14 @@ package kamon.trace import java.util.concurrent.ConcurrentLinkedQueue -import akka.actor.{ ExtensionId, ActorSystem } import akka.event.LoggingAdapter -import kamon.Kamon.Extension import kamon.metric.{ MetricsExtension, TraceMetrics } import kamon.util.{ NanoInterval, RelativeNanoTimestamp } import scala.annotation.tailrec private[kamon] class MetricsOnlyContext(traceName: String, val token: String, izOpen: Boolean, val levelOfDetail: LevelOfDetail, - val startTimestamp: RelativeNanoTimestamp, log: LoggingAdapter, metricsExtension: MetricsExtension, val actorSystem: ActorSystem) + val startTimestamp: RelativeNanoTimestamp, log: LoggingAdapter, metricsExtension: MetricsExtension) extends TraceContext { @volatile private var _name = traceName @@ -48,8 +46,6 @@ private[kamon] class MetricsOnlyContext(traceName: String, val token: String, iz def isOpen: Boolean = _isOpen def addMetadata(key: String, value: String): Unit = {} - def lookupExtension[T <: Extension](id: ExtensionId[T]): T = id(actorSystem) - def finish(): Unit = { _isOpen = false val traceElapsedTime = NanoInterval.since(startTimestamp) diff --git a/kamon-core/src/main/scala/kamon/trace/TraceContext.scala b/kamon-core/src/main/scala/kamon/trace/TraceContext.scala index ed8170a9..48e56153 100644 --- a/kamon-core/src/main/scala/kamon/trace/TraceContext.scala +++ b/kamon-core/src/main/scala/kamon/trace/TraceContext.scala @@ -17,12 +17,8 @@ package kamon.trace import java.io.ObjectStreamException -import akka.actor.{ ExtensionId, ActorSystem } -import kamon.Kamon.Extension -import kamon._ -import kamon.metric._ import kamon.trace.TraceContextAware.DefaultTraceContextAware -import kamon.util.{ NanoInterval, RelativeNanoTimestamp } +import kamon.util.RelativeNanoTimestamp trait TraceContext { def name: String @@ -39,8 +35,6 @@ trait TraceContext { def addMetadata(key: String, value: String) def startTimestamp: RelativeNanoTimestamp - - def lookupExtension[T <: Kamon.Extension](id: ExtensionId[T]): T } object TraceContext { @@ -99,9 +93,6 @@ case object EmptyTraceContext extends TraceContext { def addMetadata(key: String, value: String): Unit = {} def startTimestamp = new RelativeNanoTimestamp(0L) - override def lookupExtension[T <: Extension](id: ExtensionId[T]): T = - sys.error("Can't lookup extensions on a EmptyTraceContext.") - case object EmptySegment extends Segment { val name: String = "empty-segment" val category: String = "empty-category" diff --git a/kamon-core/src/main/scala/kamon/trace/TracerExtension.scala b/kamon-core/src/main/scala/kamon/trace/TracerExtension.scala index 41dcd6bc..be565154 100644 --- a/kamon-core/src/main/scala/kamon/trace/TracerExtension.scala +++ b/kamon-core/src/main/scala/kamon/trace/TracerExtension.scala @@ -20,20 +20,13 @@ import java.net.InetAddress import java.util.concurrent.atomic.AtomicLong import akka.actor._ -import akka.actor -import kamon.Kamon -import kamon.metric.{ Metrics, MetricsExtension } -import kamon.util.{ NanoInterval, RelativeNanoTimestamp, NanoTimestamp, GlobPathFilter } +import com.typesafe.config.Config +import kamon.metric.MetricsExtension +import kamon.util._ import scala.util.Try -object Tracer extends ExtensionId[TracerExtension] with ExtensionIdProvider { - override def get(system: ActorSystem): TracerExtension = super.get(system) - def lookup(): ExtensionId[_ <: actor.Extension] = Tracer - def createExtension(system: ExtendedActorSystem): TracerExtension = new TracerExtensionImpl(system) -} - -trait TracerExtension extends Kamon.Extension { +trait TracerExtension { def newContext(name: String): TraceContext def newContext(name: String, token: String): TraceContext def newContext(name: String, token: String, timestamp: RelativeNanoTimestamp, isOpen: Boolean, isLocal: Boolean): TraceContext @@ -42,14 +35,13 @@ trait TracerExtension extends Kamon.Extension { def unsubscribe(subscriber: ActorRef): Unit } -class TracerExtensionImpl(system: ExtendedActorSystem) extends TracerExtension { - private val _settings = TraceSettings(system) - private val _metricsExtension = Metrics.get(system) - +private[kamon] class TracerExtensionImpl(metricsExtension: MetricsExtension, config: Config) extends TracerExtension { + private val _settings = TraceSettings(config) private val _hostnamePrefix = Try(InetAddress.getLocalHost.getHostName).getOrElse("unknown-localhost") private val _tokenCounter = new AtomicLong - private val _subscriptions = system.actorOf(Props[TraceSubscriptions], "trace-subscriptions") - private val _incubator = system.actorOf(Incubator.props(_subscriptions)) + + private val _subscriptions = new LazyActorRef + private val _incubator = new LazyActorRef private def newToken: String = _hostnamePrefix + "-" + String.valueOf(_tokenCounter.incrementAndGet()) @@ -66,7 +58,7 @@ class TracerExtensionImpl(system: ExtendedActorSystem) extends TracerExtension { private def createTraceContext(traceName: String, token: String = newToken, startTimestamp: RelativeNanoTimestamp = RelativeNanoTimestamp.now, isOpen: Boolean = true, isLocal: Boolean = true): TraceContext = { - def newMetricsOnlyContext = new MetricsOnlyContext(traceName, token, isOpen, _settings.levelOfDetail, startTimestamp, null, _metricsExtension, system) + def newMetricsOnlyContext = new MetricsOnlyContext(traceName, token, isOpen, _settings.levelOfDetail, startTimestamp, null, metricsExtension) if (_settings.levelOfDetail == LevelOfDetail.MetricsOnly || !isLocal) newMetricsOnlyContext @@ -74,20 +66,44 @@ class TracerExtensionImpl(system: ExtendedActorSystem) extends TracerExtension { if (!_settings.sampler.shouldTrace) newMetricsOnlyContext else - new TracingContext(traceName, token, true, _settings.levelOfDetail, isLocal, startTimestamp, null, _metricsExtension, this, system, dispatchTracingContext) + new TracingContext(traceName, token, true, _settings.levelOfDetail, isLocal, startTimestamp, null, metricsExtension, this, dispatchTracingContext) } } - def subscribe(subscriber: ActorRef): Unit = _subscriptions ! TraceSubscriptions.Subscribe(subscriber) - def unsubscribe(subscriber: ActorRef): Unit = _subscriptions ! TraceSubscriptions.Unsubscribe(subscriber) + def subscribe(subscriber: ActorRef): Unit = + _subscriptions.tell(TraceSubscriptions.Subscribe(subscriber)) + + def unsubscribe(subscriber: ActorRef): Unit = + _subscriptions.tell(TraceSubscriptions.Unsubscribe(subscriber)) private[kamon] def dispatchTracingContext(trace: TracingContext): Unit = if (_settings.sampler.shouldReport(trace.elapsedTime)) if (trace.shouldIncubate) - _incubator ! trace + _incubator.tell(trace) else - _subscriptions ! trace.generateTraceInfo + _subscriptions.tell(trace.generateTraceInfo) + + /** + * Tracer Extension initialization. + */ + private var _system: ActorSystem = null + private lazy val _start = { + val subscriptions = _system.actorOf(Props[TraceSubscriptions], "trace-subscriptions") + _subscriptions.point(subscriptions) + _incubator.point(_system.actorOf(Incubator.props(subscriptions))) + } + + def start(system: ActorSystem): Unit = synchronized { + _system = system + _start + _system = null + } +} + +private[kamon] object TracerExtensionImpl { + def apply(metricsExtension: MetricsExtension, config: Config) = + new TracerExtensionImpl(metricsExtension, config) } case class TraceInfo(name: String, token: String, timestamp: NanoTimestamp, elapsedTime: NanoInterval, metadata: Map[String, String], segments: List[SegmentInfo]) diff --git a/kamon-core/src/main/scala/kamon/trace/TracerExtensionSettings.scala b/kamon-core/src/main/scala/kamon/trace/TracerExtensionSettings.scala index d360330d..7510ab7f 100644 --- a/kamon-core/src/main/scala/kamon/trace/TracerExtensionSettings.scala +++ b/kamon-core/src/main/scala/kamon/trace/TracerExtensionSettings.scala @@ -18,13 +18,13 @@ package kamon.trace import java.util.concurrent.TimeUnit -import akka.actor.ActorSystem +import com.typesafe.config.Config case class TraceSettings(levelOfDetail: LevelOfDetail, sampler: Sampler) object TraceSettings { - def apply(system: ActorSystem): TraceSettings = { - val tracerConfig = system.settings.config.getConfig("kamon.trace") + def apply(config: Config): TraceSettings = { + val tracerConfig = config.getConfig("kamon.trace") val detailLevel: LevelOfDetail = tracerConfig.getString("level-of-detail") match { case "metrics-only" ⇒ LevelOfDetail.MetricsOnly diff --git a/kamon-core/src/main/scala/kamon/trace/TracingContext.scala b/kamon-core/src/main/scala/kamon/trace/TracingContext.scala index dd4c3c1a..3d324886 100644 --- a/kamon-core/src/main/scala/kamon/trace/TracingContext.scala +++ b/kamon-core/src/main/scala/kamon/trace/TracingContext.scala @@ -19,7 +19,6 @@ package kamon.trace import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.atomic.AtomicInteger -import akka.actor.ActorSystem import akka.event.LoggingAdapter import kamon.util.{ NanoInterval, RelativeNanoTimestamp, NanoTimestamp } import kamon.metric.MetricsExtension @@ -28,8 +27,8 @@ import scala.collection.concurrent.TrieMap private[trace] class TracingContext(traceName: String, token: String, izOpen: Boolean, levelOfDetail: LevelOfDetail, isLocal: Boolean, startTimeztamp: RelativeNanoTimestamp, log: LoggingAdapter, metricsExtension: MetricsExtension, - traceExtension: TracerExtensionImpl, system: ActorSystem, traceInfoSink: TracingContext ⇒ Unit) - extends MetricsOnlyContext(traceName, token, izOpen, levelOfDetail, startTimeztamp, log, metricsExtension, system) { + traceExtension: TracerExtensionImpl, traceInfoSink: TracingContext ⇒ Unit) + extends MetricsOnlyContext(traceName, token, izOpen, levelOfDetail, startTimeztamp, log, metricsExtension) { private val _openSegments = new AtomicInteger(0) private val _startTimestamp = NanoTimestamp.now diff --git a/kamon-core/src/main/scala/kamon/util/LazyActorRef.scala b/kamon-core/src/main/scala/kamon/util/LazyActorRef.scala new file mode 100644 index 00000000..855bf1fc --- /dev/null +++ b/kamon-core/src/main/scala/kamon/util/LazyActorRef.scala @@ -0,0 +1,53 @@ +package kamon.util + +import java.util +import java.util.concurrent.ConcurrentLinkedQueue + +import akka.actor.{ Actor, ActorRef } +import org.HdrHistogram.WriterReaderPhaser + +import scala.annotation.tailrec + +/** + * A LazyActorRef accumulates messages sent to an actor that doesn't exist yet. Once the actor is created and + * the LazyActorRef is pointed to it, all the accumulated messages are flushed and any new message sent to the + * LazyActorRef will immediately be sent to the pointed ActorRef. + * + * This is intended to be used during Kamon's initialization where some components need to use ActorRefs to work + * (like subscriptions and the trace incubator) but our internal ActorSystem is not yet ready to create the + * required actors. + */ +class LazyActorRef { + private val _refPhaser = new WriterReaderPhaser + private val _backlog = new ConcurrentLinkedQueue[(Any, ActorRef)]() + @volatile private var _target: Option[ActorRef] = None + + def tell(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit = { + val criticalEnter = _refPhaser.writerCriticalSectionEnter() + try { + _target.map(_.tell(message, sender)) getOrElse { + _backlog.add((message, sender)) + } + + } finally { _refPhaser.writerCriticalSectionExit(criticalEnter) } + } + + def point(target: ActorRef): Unit = { + @tailrec def drain(q: util.Queue[(Any, ActorRef)]): Unit = if (!q.isEmpty) { + val (msg, sender) = q.poll() + target.tell(msg, sender) + drain(q) + } + + try { + _refPhaser.readerLock() + + if (_target.isEmpty) { + _target = Some(target) + _refPhaser.flipPhase(1000L) + drain(_backlog) + + } else sys.error("A LazyActorRef cannot be pointed more than once.") + } finally { _refPhaser.readerUnlock() } + } +} diff --git a/kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala b/kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala index 7dbcafd7..53ae5273 100644 --- a/kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala +++ b/kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala @@ -19,6 +19,7 @@ package kamon.metric import akka.actor._ import akka.testkit.{ TestProbe, ImplicitSender } import com.typesafe.config.ConfigFactory +import kamon.Kamon import kamon.metric.SubscriptionsDispatcher.TickMetricSnapshot import kamon.testkit.BaseKamonSpec import scala.concurrent.duration._ @@ -32,7 +33,7 @@ class SubscriptionsProtocolSpec extends BaseKamonSpec("subscriptions-protocol-sp |} """.stripMargin) - val metricsModule = kamon.metrics + lazy val metricsModule = Kamon.metrics import metricsModule.{ register, subscribe, unsubscribe } "the Subscriptions messaging protocol" should { diff --git a/kamon-core/src/test/scala/kamon/metric/TickMetricSnapshotBufferSpec.scala b/kamon-core/src/test/scala/kamon/metric/TickMetricSnapshotBufferSpec.scala index 2e1f246d..0c9ced32 100644 --- a/kamon-core/src/test/scala/kamon/metric/TickMetricSnapshotBufferSpec.scala +++ b/kamon-core/src/test/scala/kamon/metric/TickMetricSnapshotBufferSpec.scala @@ -17,6 +17,7 @@ package kamon.metric import com.typesafe.config.ConfigFactory +import kamon.Kamon import kamon.metric.instrument.Histogram.MutableRecord import kamon.testkit.BaseKamonSpec import kamon.util.MilliTimestamp @@ -85,9 +86,9 @@ class TickMetricSnapshotBufferSpec extends BaseKamonSpec("trace-metrics-spec") w } trait SnapshotFixtures { - val collectionContext = kamon.metrics.buildDefaultCollectionContext + val collectionContext = Kamon.metrics.buildDefaultCollectionContext val testTraceIdentity = Entity("buffer-spec-test-trace", "trace") - val traceRecorder = kamon.metrics.register(TraceMetrics, "buffer-spec-test-trace").get.recorder + val traceRecorder = Kamon.metrics.register(TraceMetrics, "buffer-spec-test-trace").get.recorder val firstEmpty = TickMetricSnapshot(new MilliTimestamp(1000), new MilliTimestamp(2000), Map.empty) val secondEmpty = TickMetricSnapshot(new MilliTimestamp(2000), new MilliTimestamp(3000), Map.empty) diff --git a/kamon-core/src/test/scala/kamon/metric/UserMetricsSpec.scala b/kamon-core/src/test/scala/kamon/metric/UserMetricsSpec.scala index 7b6cbebc..455518f8 100644 --- a/kamon-core/src/test/scala/kamon/metric/UserMetricsSpec.scala +++ b/kamon-core/src/test/scala/kamon/metric/UserMetricsSpec.scala @@ -17,6 +17,7 @@ package kamon.metric import com.typesafe.config.ConfigFactory +import kamon.Kamon import kamon.metric.instrument.Histogram.DynamicRange import kamon.testkit.BaseKamonSpec import scala.concurrent.duration._ @@ -34,54 +35,54 @@ class UserMetricsSpec extends BaseKamonSpec("user-metrics-spec") { "the UserMetrics extension" should { "allow registering a fully configured Histogram and get the same Histogram if registering again" in { - val histogramA = kamon.userMetrics.histogram("histogram-with-settings", DynamicRange(1, 10000, 2)) - val histogramB = kamon.userMetrics.histogram("histogram-with-settings", DynamicRange(1, 10000, 2)) + val histogramA = Kamon.userMetrics.histogram("histogram-with-settings", DynamicRange(1, 10000, 2)) + val histogramB = Kamon.userMetrics.histogram("histogram-with-settings", DynamicRange(1, 10000, 2)) histogramA shouldBe theSameInstanceAs(histogramB) } "return the original Histogram when registering a fully configured Histogram for second time but with different settings" in { - val histogramA = kamon.userMetrics.histogram("histogram-with-settings", DynamicRange(1, 10000, 2)) - val histogramB = kamon.userMetrics.histogram("histogram-with-settings", DynamicRange(1, 50000, 2)) + val histogramA = Kamon.userMetrics.histogram("histogram-with-settings", DynamicRange(1, 10000, 2)) + val histogramB = Kamon.userMetrics.histogram("histogram-with-settings", DynamicRange(1, 50000, 2)) histogramA shouldBe theSameInstanceAs(histogramB) } "allow registering a Histogram that takes the default configuration from the kamon.metrics.precision settings" in { - kamon.userMetrics.histogram("histogram-with-default-configuration") + Kamon.userMetrics.histogram("histogram-with-default-configuration") } "allow registering a Counter and get the same Counter if registering again" in { - val counterA = kamon.userMetrics.counter("counter") - val counterB = kamon.userMetrics.counter("counter") + val counterA = Kamon.userMetrics.counter("counter") + val counterB = Kamon.userMetrics.counter("counter") counterA shouldBe theSameInstanceAs(counterB) } "allow registering a fully configured MinMaxCounter and get the same MinMaxCounter if registering again" in { - val minMaxCounterA = kamon.userMetrics.minMaxCounter("min-max-counter-with-settings", DynamicRange(1, 10000, 2), 1 second) - val minMaxCounterB = kamon.userMetrics.minMaxCounter("min-max-counter-with-settings", DynamicRange(1, 10000, 2), 1 second) + val minMaxCounterA = Kamon.userMetrics.minMaxCounter("min-max-counter-with-settings", DynamicRange(1, 10000, 2), 1 second) + val minMaxCounterB = Kamon.userMetrics.minMaxCounter("min-max-counter-with-settings", DynamicRange(1, 10000, 2), 1 second) minMaxCounterA shouldBe theSameInstanceAs(minMaxCounterB) } "return the original MinMaxCounter when registering a fully configured MinMaxCounter for second time but with different settings" in { - val minMaxCounterA = kamon.userMetrics.minMaxCounter("min-max-counter-with-settings", DynamicRange(1, 10000, 2), 1 second) - val minMaxCounterB = kamon.userMetrics.minMaxCounter("min-max-counter-with-settings", DynamicRange(1, 50000, 2), 1 second) + val minMaxCounterA = Kamon.userMetrics.minMaxCounter("min-max-counter-with-settings", DynamicRange(1, 10000, 2), 1 second) + val minMaxCounterB = Kamon.userMetrics.minMaxCounter("min-max-counter-with-settings", DynamicRange(1, 50000, 2), 1 second) minMaxCounterA shouldBe theSameInstanceAs(minMaxCounterB) } "allow registering a MinMaxCounter that takes the default configuration from the kamon.metrics.precision settings" in { - kamon.userMetrics.minMaxCounter("min-max-counter-with-default-configuration") + Kamon.userMetrics.minMaxCounter("min-max-counter-with-default-configuration") } "allow registering a fully configured Gauge and get the same Gauge if registering again" in { - val gaugeA = kamon.userMetrics.gauge("gauge-with-settings", DynamicRange(1, 10000, 2), 1 second, { + val gaugeA = Kamon.userMetrics.gauge("gauge-with-settings", DynamicRange(1, 10000, 2), 1 second, { () ⇒ 1L }) - val gaugeB = kamon.userMetrics.gauge("gauge-with-settings", DynamicRange(1, 10000, 2), 1 second, { + val gaugeB = Kamon.userMetrics.gauge("gauge-with-settings", DynamicRange(1, 10000, 2), 1 second, { () ⇒ 1L }) @@ -89,11 +90,11 @@ class UserMetricsSpec extends BaseKamonSpec("user-metrics-spec") { } "return the original Gauge when registering a fully configured Gauge for second time but with different settings" in { - val gaugeA = kamon.userMetrics.gauge("gauge-with-settings", DynamicRange(1, 10000, 2), 1 second, { + val gaugeA = Kamon.userMetrics.gauge("gauge-with-settings", DynamicRange(1, 10000, 2), 1 second, { () ⇒ 1L }) - val gaugeB = kamon.userMetrics.gauge("gauge-with-settings", DynamicRange(1, 10000, 2), 1 second, { + val gaugeB = Kamon.userMetrics.gauge("gauge-with-settings", DynamicRange(1, 10000, 2), 1 second, { () ⇒ 1L }) @@ -101,26 +102,26 @@ class UserMetricsSpec extends BaseKamonSpec("user-metrics-spec") { } "allow registering a Gauge that takes the default configuration from the kamon.metrics.precision settings" in { - kamon.userMetrics.gauge("gauge-with-default-configuration", { + Kamon.userMetrics.gauge("gauge-with-default-configuration", { () ⇒ 2L }) } "allow un-registering user metrics" in { - val counter = kamon.userMetrics.counter("counter-for-remove") - val histogram = kamon.userMetrics.histogram("histogram-for-remove") - val minMaxCounter = kamon.userMetrics.minMaxCounter("min-max-counter-for-remove") - val gauge = kamon.userMetrics.gauge("gauge-for-remove", { () ⇒ 2L }) - - kamon.userMetrics.removeCounter("counter-for-remove") - kamon.userMetrics.removeHistogram("histogram-for-remove") - kamon.userMetrics.removeMinMaxCounter("min-max-counter-for-remove") - kamon.userMetrics.removeGauge("gauge-for-remove") - - counter should not be (theSameInstanceAs(kamon.userMetrics.counter("counter-for-remove"))) - histogram should not be (theSameInstanceAs(kamon.userMetrics.histogram("histogram-for-remove"))) - minMaxCounter should not be (theSameInstanceAs(kamon.userMetrics.minMaxCounter("min-max-counter-for-remove"))) - gauge should not be (theSameInstanceAs(kamon.userMetrics.gauge("gauge-for-remove", { () ⇒ 2L }))) + val counter = Kamon.userMetrics.counter("counter-for-remove") + val histogram = Kamon.userMetrics.histogram("histogram-for-remove") + val minMaxCounter = Kamon.userMetrics.minMaxCounter("min-max-counter-for-remove") + val gauge = Kamon.userMetrics.gauge("gauge-for-remove", { () ⇒ 2L }) + + Kamon.userMetrics.removeCounter("counter-for-remove") + Kamon.userMetrics.removeHistogram("histogram-for-remove") + Kamon.userMetrics.removeMinMaxCounter("min-max-counter-for-remove") + Kamon.userMetrics.removeGauge("gauge-for-remove") + + counter should not be (theSameInstanceAs(Kamon.userMetrics.counter("counter-for-remove"))) + histogram should not be (theSameInstanceAs(Kamon.userMetrics.histogram("histogram-for-remove"))) + minMaxCounter should not be (theSameInstanceAs(Kamon.userMetrics.minMaxCounter("min-max-counter-for-remove"))) + gauge should not be (theSameInstanceAs(Kamon.userMetrics.gauge("gauge-for-remove", { () ⇒ 2L }))) } } } diff --git a/kamon-core/src/test/scala/kamon/metric/instrument/GaugeSpec.scala b/kamon-core/src/test/scala/kamon/metric/instrument/GaugeSpec.scala index bb494d0b..ec07d66c 100644 --- a/kamon-core/src/test/scala/kamon/metric/instrument/GaugeSpec.scala +++ b/kamon-core/src/test/scala/kamon/metric/instrument/GaugeSpec.scala @@ -17,6 +17,7 @@ package kamon.metric.instrument import java.util.concurrent.atomic.AtomicLong +import kamon.Kamon import kamon.metric.instrument.Histogram.DynamicRange import kamon.testkit.BaseKamonSpec import scala.concurrent.duration._ @@ -48,7 +49,7 @@ class GaugeSpec extends BaseKamonSpec("gauge-spec") { Thread.sleep(1.second.toMillis) gauge.cleanup - val snapshot = gauge.collect(kamon.metrics.buildDefaultCollectionContext) + val snapshot = gauge.collect(Kamon.metrics.buildDefaultCollectionContext) snapshot.numberOfMeasurements should be(10L +- 1L) snapshot.min should be(1) @@ -58,7 +59,7 @@ class GaugeSpec extends BaseKamonSpec("gauge-spec") { "not record the current value when doing a collection" in new GaugeFixture { val (numberOfValuesRecorded, gauge) = createGauge(10 seconds) - val snapshot = gauge.collect(kamon.metrics.buildDefaultCollectionContext) + val snapshot = gauge.collect(Kamon.metrics.buildDefaultCollectionContext) snapshot.numberOfMeasurements should be(0) numberOfValuesRecorded.get() should be(0) } @@ -67,7 +68,7 @@ class GaugeSpec extends BaseKamonSpec("gauge-spec") { trait GaugeFixture { def createGauge(refreshInterval: FiniteDuration = 100 millis): (AtomicLong, Gauge) = { val recordedValuesCounter = new AtomicLong(0) - val gauge = Gauge(DynamicRange(1, 100, 2), refreshInterval, kamon.metrics.settings.refreshScheduler, { + val gauge = Gauge(DynamicRange(1, 100, 2), refreshInterval, Kamon.metrics.settings.refreshScheduler, { () ⇒ recordedValuesCounter.addAndGet(1) }) diff --git a/kamon-core/src/test/scala/kamon/metric/instrument/MinMaxCounterSpec.scala b/kamon-core/src/test/scala/kamon/metric/instrument/MinMaxCounterSpec.scala index 7a3d7aa3..7acfc229 100644 --- a/kamon-core/src/test/scala/kamon/metric/instrument/MinMaxCounterSpec.scala +++ b/kamon-core/src/test/scala/kamon/metric/instrument/MinMaxCounterSpec.scala @@ -19,6 +19,7 @@ import java.nio.LongBuffer import akka.actor._ import akka.testkit.TestProbe +import kamon.Kamon import kamon.metric.instrument.Histogram.{ DynamicRange, MutableRecord } import kamon.testkit.BaseKamonSpec import scala.concurrent.duration._ @@ -109,7 +110,7 @@ class MinMaxCounterSpec extends BaseKamonSpec("min-max-counter-spec") { val buffer: LongBuffer = LongBuffer.allocate(64) } - val mmCounter = MinMaxCounter(DynamicRange(1, 1000, 2), 1 hour, kamon.metrics.settings.refreshScheduler) + val mmCounter = MinMaxCounter(DynamicRange(1, 1000, 2), 1 hour, Kamon.metrics.settings.refreshScheduler) mmCounter.cleanup // cancel the refresh schedule def collectCounterSnapshot(): Histogram.Snapshot = mmCounter.collect(collectionContext) diff --git a/kamon-core/src/test/scala/kamon/testkit/BaseKamonSpec.scala b/kamon-core/src/test/scala/kamon/testkit/BaseKamonSpec.scala index 9142ff16..eab6b754 100644 --- a/kamon-core/src/test/scala/kamon/testkit/BaseKamonSpec.scala +++ b/kamon-core/src/test/scala/kamon/testkit/BaseKamonSpec.scala @@ -22,29 +22,39 @@ import com.typesafe.config.{ Config, ConfigFactory } import kamon.Kamon import kamon.metric.{ SubscriptionsDispatcher, EntitySnapshot, MetricsExtensionImpl } import kamon.trace.TraceContext +import kamon.util.LazyActorRef import org.scalatest.{ BeforeAndAfterAll, Matchers, WordSpecLike } +import scala.reflect.ClassTag + abstract class BaseKamonSpec(actorSystemName: String) extends TestKitBase with WordSpecLike with Matchers with ImplicitSender with BeforeAndAfterAll { - lazy val kamon = Kamon(actorSystemName, config) - lazy val collectionContext = kamon.metrics.buildDefaultCollectionContext - implicit lazy val system: ActorSystem = kamon.actorSystem + lazy val collectionContext = Kamon.metrics.buildDefaultCollectionContext + implicit lazy val system: ActorSystem = { + Kamon.start(config.withFallback(ConfigFactory.load())) + ActorSystem(actorSystemName, config) + } def config: Config = - ConfigFactory.load() + ConfigFactory.empty() def newContext(name: String): TraceContext = - kamon.tracer.newContext(name) + Kamon.tracer.newContext(name) def newContext(name: String, token: String): TraceContext = - kamon.tracer.newContext(name, token) + Kamon.tracer.newContext(name, token) def takeSnapshotOf(name: String, category: String): EntitySnapshot = { - val recorder = kamon.metrics.find(name, category).get + val recorder = Kamon.metrics.find(name, category).get recorder.collect(collectionContext) } - def flushSubscriptions(): Unit = - system.actorSelection("/user/kamon/subscriptions-dispatcher") ! SubscriptionsDispatcher.Tick + def flushSubscriptions(): Unit = { + val subscriptionsField = Kamon.metrics.getClass.getDeclaredField("_subscriptions") + subscriptionsField.setAccessible(true) + val subscriptions = subscriptionsField.get(Kamon.metrics).asInstanceOf[LazyActorRef] + + subscriptions.tell(SubscriptionsDispatcher.Tick) + } override protected def afterAll(): Unit = system.shutdown() } diff --git a/kamon-core/src/test/scala/kamon/trace/SimpleTraceSpec.scala b/kamon-core/src/test/scala/kamon/trace/SimpleTraceSpec.scala index 0cb4ce34..1d270106 100644 --- a/kamon-core/src/test/scala/kamon/trace/SimpleTraceSpec.scala +++ b/kamon-core/src/test/scala/kamon/trace/SimpleTraceSpec.scala @@ -40,7 +40,7 @@ class SimpleTraceSpec extends BaseKamonSpec("simple-trace-spec") { "the simple tracing" should { "send a TraceInfo when the trace has finished and all segments are finished" in { - Kamon(Tracer)(system).subscribe(testActor) + Kamon.tracer.subscribe(testActor) TraceContext.withContext(newContext("simple-trace-without-segments")) { TraceContext.currentContext.startSegment("segment-one", "test-segment", "test").finish() @@ -49,7 +49,7 @@ class SimpleTraceSpec extends BaseKamonSpec("simple-trace-spec") { } val traceInfo = expectMsgType[TraceInfo] - Kamon(Tracer)(system).unsubscribe(testActor) + Kamon.tracer.unsubscribe(testActor) traceInfo.name should be("simple-trace-without-segments") traceInfo.segments.size should be(2) @@ -58,7 +58,7 @@ class SimpleTraceSpec extends BaseKamonSpec("simple-trace-spec") { } "incubate the tracing context if there are open segments after finishing" in { - Kamon(Tracer)(system).subscribe(testActor) + Kamon.tracer.subscribe(testActor) val secondSegment = TraceContext.withContext(newContext("simple-trace-without-segments")) { TraceContext.currentContext.startSegment("segment-one", "test-segment", "test").finish() @@ -72,7 +72,7 @@ class SimpleTraceSpec extends BaseKamonSpec("simple-trace-spec") { within(10 seconds) { val traceInfo = expectMsgType[TraceInfo] - Kamon(Tracer)(system).unsubscribe(testActor) + Kamon.tracer.unsubscribe(testActor) traceInfo.name should be("simple-trace-without-segments") traceInfo.segments.size should be(2) diff --git a/kamon-datadog/src/main/scala/kamon/datadog/Datadog.scala b/kamon-datadog/src/main/scala/kamon/datadog/Datadog.scala index b7375d9b..53ffd4e6 100644 --- a/kamon-datadog/src/main/scala/kamon/datadog/Datadog.scala +++ b/kamon-datadog/src/main/scala/kamon/datadog/Datadog.scala @@ -50,7 +50,7 @@ class DatadogExtension(system: ExtendedActorSystem) extends Kamon.Extension { val subscriptions = datadogConfig.getConfig("subscriptions") subscriptions.firstLevelKeys.map { subscriptionCategory ⇒ subscriptions.getStringList(subscriptionCategory).asScala.foreach { pattern ⇒ - Kamon(Metrics).subscribe(subscriptionCategory, pattern, datadogMetricsListener, permanently = true) + Kamon.metrics.subscribe(subscriptionCategory, pattern, datadogMetricsListener, permanently = true) } } diff --git a/kamon-datadog/src/test/scala/kamon/datadog/DatadogMetricSenderSpec.scala b/kamon-datadog/src/test/scala/kamon/datadog/DatadogMetricSenderSpec.scala index 1fcc0c5d..b35902f9 100644 --- a/kamon-datadog/src/test/scala/kamon/datadog/DatadogMetricSenderSpec.scala +++ b/kamon-datadog/src/test/scala/kamon/datadog/DatadogMetricSenderSpec.scala @@ -20,6 +20,7 @@ import akka.testkit.{ TestKitBase, TestProbe } import akka.actor.{ Props, ActorRef, ActorSystem } import kamon.Kamon import kamon.metric.instrument._ +import kamon.testkit.BaseKamonSpec import kamon.util.MilliTimestamp import org.scalatest.{ Matchers, WordSpecLike } import kamon.metric._ @@ -29,22 +30,21 @@ import java.lang.management.ManagementFactory import java.net.InetSocketAddress import com.typesafe.config.ConfigFactory -class DatadogMetricSenderSpec extends TestKitBase with WordSpecLike with Matchers { - implicit lazy val system: ActorSystem = ActorSystem("datadog-metric-sender-spec", ConfigFactory.parseString( - """ - |kamon { - | metrics { - | disable-aspectj-weaver-missing-error = true - | } - | - | datadog { - | max-packet-size = 256 bytes - | } - |} - | - """.stripMargin)) - - val collectionContext = Kamon(Metrics).buildDefaultCollectionContext +class DatadogMetricSenderSpec extends BaseKamonSpec("datadog-metric-sender-spec") { + override lazy val config = + ConfigFactory.parseString( + """ + |kamon { + | metrics { + | disable-aspectj-weaver-missing-error = true + | } + | + | datadog { + | max-packet-size = 256 bytes + | } + |} + | + """.stripMargin) "the DataDogMetricSender" should { "send latency measurements" in new UdpListenerFixture { @@ -106,7 +106,7 @@ class DatadogMetricSenderSpec extends TestKitBase with WordSpecLike with Matcher val testMaxPacketSize = system.settings.config.getBytes("kamon.datadog.max-packet-size") def buildRecorder(name: String): (Entity, TestEntityRecorder) = { - val registration = Kamon(Metrics).register(TestEntityRecorder, name).get + val registration = Kamon.metrics.register(TestEntityRecorder, name).get (registration.entity, registration.recorder) } diff --git a/kamon-jdbc/src/main/scala/kamon/jdbc/instrumentation/StatementInstrumentation.scala b/kamon-jdbc/src/main/scala/kamon/jdbc/instrumentation/StatementInstrumentation.scala index 386cf019..d169a4c7 100644 --- a/kamon-jdbc/src/main/scala/kamon/jdbc/instrumentation/StatementInstrumentation.scala +++ b/kamon-jdbc/src/main/scala/kamon/jdbc/instrumentation/StatementInstrumentation.scala @@ -17,9 +17,9 @@ package kamon.jdbc.instrumentation import java.util.concurrent.TimeUnit.{ NANOSECONDS ⇒ nanos } +import kamon.Kamon import kamon.jdbc.{ JdbcExtension, Jdbc } import kamon.jdbc.metric.StatementsMetrics -import kamon.metric.Metrics import kamon.trace.{ TraceContext, SegmentCategory } import org.aspectj.lang.ProceedingJoinPoint import org.aspectj.lang.annotation.{ Around, Aspect, Pointcut } @@ -44,8 +44,8 @@ class StatementInstrumentation { @Around("onExecuteStatement(sql) || onExecutePreparedStatement(sql) || onExecutePreparedCall(sql)") def aroundExecuteStatement(pjp: ProceedingJoinPoint, sql: String): Any = { TraceContext.map { ctx ⇒ - val metricsExtension = ctx.lookupExtension(Metrics) - val jdbcExtension = ctx.lookupExtension(Jdbc) + val metricsExtension = Kamon.metrics + val jdbcExtension = Kamon(Jdbc) implicit val statementRecorder = metricsExtension.register(StatementsMetrics, "jdbc-statements").map(_.recorder) sql.replaceAll(CommentPattern, Empty) match { diff --git a/kamon-log-reporter/src/main/scala/kamon/logreporter/LogReporter.scala b/kamon-log-reporter/src/main/scala/kamon/logreporter/LogReporter.scala index dc977a52..d5b07f6d 100644 --- a/kamon-log-reporter/src/main/scala/kamon/logreporter/LogReporter.scala +++ b/kamon-log-reporter/src/main/scala/kamon/logreporter/LogReporter.scala @@ -35,13 +35,13 @@ class LogReporterExtension(system: ExtendedActorSystem) extends Kamon.Extension val logReporterConfig = system.settings.config.getConfig("kamon.log-reporter") val subscriber = system.actorOf(Props[LogReporterSubscriber], "kamon-log-reporter") - Kamon(Metrics)(system).subscribe("trace", "**", subscriber, permanently = true) - Kamon(Metrics)(system).subscribe("actor", "**", subscriber, permanently = true) - Kamon(Metrics)(system).subscribe("user-metrics", "**", subscriber, permanently = true) + Kamon.metrics.subscribe("trace", "**", subscriber, permanently = true) + Kamon.metrics.subscribe("actor", "**", subscriber, permanently = true) + Kamon.metrics.subscribe("user-metrics", "**", subscriber, permanently = true) val includeSystemMetrics = logReporterConfig.getBoolean("report-system-metrics") if (includeSystemMetrics) { - Kamon(Metrics)(system).subscribe("system-metric", "**", subscriber, permanently = true) + Kamon.metrics.subscribe("system-metric", "**", subscriber, permanently = true) } } diff --git a/kamon-newrelic/src/main/scala/kamon/newrelic/CustomMetricExtractor.scala b/kamon-newrelic/src/main/scala/kamon/newrelic/CustomMetricExtractor.scala index 551bb546..3b1b8cb3 100644 --- a/kamon-newrelic/src/main/scala/kamon/newrelic/CustomMetricExtractor.scala +++ b/kamon-newrelic/src/main/scala/kamon/newrelic/CustomMetricExtractor.scala @@ -16,13 +16,13 @@ package kamon.newrelic -import kamon.metric.{ UserMetrics, EntitySnapshot, Entity } +import kamon.metric.{ UserMetricsExtensionImpl, EntitySnapshot, Entity } import kamon.metric.instrument.CollectionContext object CustomMetricExtractor extends MetricExtractor { def extract(settings: AgentSettings, collectionContext: CollectionContext, metrics: Map[Entity, EntitySnapshot]): Map[MetricID, MetricData] = { - metrics.get(UserMetrics.entity).map { allUserMetrics ⇒ + metrics.get(UserMetricsExtensionImpl.UserMetricEntity).map { allUserMetrics ⇒ allUserMetrics.metrics.map { case (key, snapshot) ⇒ Metric(snapshot, key.unitOfMeasurement, s"Custom/${key.name}", None) } diff --git a/kamon-newrelic/src/main/scala/kamon/newrelic/MetricReporter.scala b/kamon-newrelic/src/main/scala/kamon/newrelic/MetricReporter.scala index 51c1ad21..31c04f7a 100644 --- a/kamon-newrelic/src/main/scala/kamon/newrelic/MetricReporter.scala +++ b/kamon-newrelic/src/main/scala/kamon/newrelic/MetricReporter.scala @@ -19,7 +19,7 @@ import JsonProtocol._ class MetricReporter(settings: AgentSettings) extends Actor with ActorLogging with SprayJsonSupport { import context.dispatcher - val metricsExtension = Kamon(Metrics)(context.system) + val metricsExtension = Kamon.metrics val collectionContext = metricsExtension.buildDefaultCollectionContext val metricsSubscriber = { val tickInterval = context.system.settings.config.getDuration("kamon.metric.tick-interval", TimeUnit.MILLISECONDS) diff --git a/kamon-newrelic/src/test/scala/kamon/newrelic/MetricReporterSpec.scala b/kamon-newrelic/src/test/scala/kamon/newrelic/MetricReporterSpec.scala index a8aa00d7..04380677 100644 --- a/kamon-newrelic/src/test/scala/kamon/newrelic/MetricReporterSpec.scala +++ b/kamon-newrelic/src/test/scala/kamon/newrelic/MetricReporterSpec.scala @@ -16,43 +16,44 @@ package kamon.newrelic -import akka.actor.{ ActorRef, ActorSystem } +import akka.actor.ActorRef import akka.io.IO import akka.testkit._ import akka.util.Timeout import com.typesafe.config.ConfigFactory -import kamon.metric.{ Entity, Metrics, TraceMetrics } +import kamon.metric.{ Entity, TraceMetrics } +import kamon.testkit.BaseKamonSpec import kamon.util.MilliTimestamp import kamon.Kamon import kamon.metric.SubscriptionsDispatcher.TickMetricSnapshot -import org.scalatest.{ Matchers, WordSpecLike } import spray.can.Http import spray.http.Uri.Query import spray.http._ import spray.httpx.encoding.Deflate -import spray.httpx.{ RequestBuilding, SprayJsonSupport } +import spray.httpx.SprayJsonSupport import testkit.AkkaExtensionSwap import scala.concurrent.duration._ import spray.json._ -class MetricReporterSpec extends TestKitBase with WordSpecLike with Matchers with RequestBuilding with SprayJsonSupport { +class MetricReporterSpec extends BaseKamonSpec("metric-reporter-spec") with SprayJsonSupport { import kamon.newrelic.JsonProtocol._ - implicit lazy val system: ActorSystem = ActorSystem("metric-reporter-spec", ConfigFactory.parseString( - """ - |akka { - | loggers = ["akka.testkit.TestEventListener"] - | loglevel = "INFO" - |} - |kamon { - | metric { - | tick-interval = 1 hour - | } - | - | modules.kamon-newrelic.auto-start = no - |} - | - """.stripMargin)) + override lazy val config = + ConfigFactory.parseString( + """ + |akka { + | loggers = ["akka.testkit.TestEventListener"] + | loglevel = "INFO" + |} + |kamon { + | metric { + | tick-interval = 1 hour + | } + | + | modules.kamon-newrelic.auto-start = no + |} + | + """.stripMargin) val agentSettings = AgentSettings("1111111111", "kamon", "test-host", 1, Timeout(5 seconds), 1, 30 seconds, 1D) val baseQuery = Query( @@ -138,8 +139,8 @@ class MetricReporterSpec extends TestKitBase with WordSpecLike with Matchers wit trait FakeTickSnapshotsFixture { val testTraceID = Entity("example-trace", "trace") - val recorder = Kamon(Metrics).register(TraceMetrics, testTraceID.name).get.recorder - val collectionContext = Kamon(Metrics).buildDefaultCollectionContext + val recorder = Kamon.metrics.register(TraceMetrics, testTraceID.name).get.recorder + val collectionContext = Kamon.metrics.buildDefaultCollectionContext def collectRecorder = recorder.collect(collectionContext) diff --git a/kamon-play/src/main/scala/kamon/play/Play.scala b/kamon-play/src/main/scala/kamon/play/Play.scala index 7ca81028..3c3e4fa7 100644 --- a/kamon-play/src/main/scala/kamon/play/Play.scala +++ b/kamon-play/src/main/scala/kamon/play/Play.scala @@ -20,7 +20,7 @@ import akka.actor.{ ExtendedActorSystem, Extension, ExtensionId, ExtensionIdProv import akka.event.Logging import kamon.Kamon import kamon.http.HttpServerMetrics -import kamon.metric.{ Entity, Metrics } +import kamon.metric.Entity import play.api.libs.ws.WSRequest import play.api.mvc.RequestHeader @@ -37,11 +37,11 @@ class PlayExtension(private val system: ExtendedActorSystem) extends Kamon.Exten private val config = system.settings.config.getConfig("kamon.play") val httpServerMetrics = { - val metricsExtension = Metrics.get(system) + val metricsExtension = Kamon.metrics val factory = metricsExtension.instrumentFactory(HttpServerMetrics.category) val entity = Entity("play-server", HttpServerMetrics.category) - Metrics.get(system).register(entity, new HttpServerMetrics(factory)).recorder + metricsExtension.register(entity, new HttpServerMetrics(factory)).recorder } val defaultDispatcher = system.dispatchers.lookup(config.getString("dispatcher")) diff --git a/kamon-play/src/main/scala/kamon/play/instrumentation/RequestInstrumentation.scala b/kamon-play/src/main/scala/kamon/play/instrumentation/RequestInstrumentation.scala index 38f499b4..e9b2d570 100644 --- a/kamon-play/src/main/scala/kamon/play/instrumentation/RequestInstrumentation.scala +++ b/kamon-play/src/main/scala/kamon/play/instrumentation/RequestInstrumentation.scala @@ -36,14 +36,13 @@ class RequestInstrumentation { @After("call(* play.api.GlobalSettings.onStart(*)) && args(application)") def afterApplicationStart(application: play.api.Application): Unit = { - Kamon(Play)(Akka.system()) + Kamon(Play) } @Before("call(* play.api.GlobalSettings.onRouteRequest(..)) && args(requestHeader)") def beforeRouteRequest(requestHeader: RequestHeader): Unit = { - implicit val system = Akka.system() + import Kamon.tracer val playExtension = Kamon(Play) - val tracer = Kamon(Tracer) val defaultTraceName = playExtension.generateTraceName(requestHeader) val token = if (playExtension.includeTraceToken) { @@ -58,14 +57,14 @@ class RequestInstrumentation { def aroundDoFilter(pjp: ProceedingJoinPoint, next: EssentialAction): Any = { val essentialAction = (requestHeader: RequestHeader) ⇒ { - val executor = Kamon(Play)(Akka.system()).defaultDispatcher + val playExtension = Kamon(Play) + val executor = playExtension.defaultDispatcher def onResult(result: Result): Result = { TraceContext.map { ctx ⇒ ctx.finish() - val playExtension = ctx.lookupExtension(Play) - recordHttpServerMetrics(result.header, ctx.name, playExtension) + recordHttpServerMetrics(result.header, ctx.name) if (playExtension.includeTraceToken) result.withHeaders(playExtension.traceTokenHeaderName -> ctx.token) else result @@ -87,12 +86,12 @@ class RequestInstrumentation { @Before("call(* play.api.GlobalSettings.onError(..)) && args(request, ex)") def beforeOnError(request: TraceContextAware, ex: Throwable): Unit = { TraceContext.map { ctx ⇒ - recordHttpServerMetrics(InternalServerError.header, ctx.name, ctx.lookupExtension(Play)) + recordHttpServerMetrics(InternalServerError.header, ctx.name) } } - def recordHttpServerMetrics(header: ResponseHeader, traceName: String, playExtension: PlayExtension): Unit = - playExtension.httpServerMetrics.recordResponse(traceName, header.status.toString) + def recordHttpServerMetrics(header: ResponseHeader, traceName: String): Unit = + Kamon(Play).httpServerMetrics.recordResponse(traceName, header.status.toString) def storeDiagnosticData(request: RequestHeader): Unit = { val agent = request.headers.get(UserAgent).getOrElse(Unknown) diff --git a/kamon-play/src/main/scala/kamon/play/instrumentation/WSInstrumentation.scala b/kamon-play/src/main/scala/kamon/play/instrumentation/WSInstrumentation.scala index fc58f9da..8b76ab8b 100644 --- a/kamon-play/src/main/scala/kamon/play/instrumentation/WSInstrumentation.scala +++ b/kamon-play/src/main/scala/kamon/play/instrumentation/WSInstrumentation.scala @@ -16,6 +16,7 @@ package kamon.play.instrumentation +import kamon.Kamon import kamon.play.Play import kamon.trace.{ TraceContext, SegmentCategory } import org.aspectj.lang.ProceedingJoinPoint @@ -33,7 +34,7 @@ class WSInstrumentation { @Around("onExecuteRequest(request)") def aroundExecuteRequest(pjp: ProceedingJoinPoint, request: WSRequest): Any = { TraceContext.map { ctx ⇒ - val playExtension = ctx.lookupExtension(Play) + val playExtension = Kamon(Play) val executor = playExtension.defaultDispatcher val segmentName = playExtension.generateHttpClientSegmentName(request) val segment = ctx.startSegment(segmentName, SegmentCategory.HttpClient, Play.SegmentLibraryName) diff --git a/kamon-play/src/test/scala/kamon/play/LoggerLikeInstrumentationSpec.scala b/kamon-play/src/test/scala/kamon/play/LoggerLikeInstrumentationSpec.scala index 622c2501..de85743c 100644 --- a/kamon-play/src/test/scala/kamon/play/LoggerLikeInstrumentationSpec.scala +++ b/kamon-play/src/test/scala/kamon/play/LoggerLikeInstrumentationSpec.scala @@ -19,6 +19,7 @@ import ch.qos.logback.classic.spi.ILoggingEvent import ch.qos.logback.classic.{ AsyncAppender, LoggerContext } import ch.qos.logback.core.read.ListAppender import ch.qos.logback.core.status.NopStatusListener +import kamon.Kamon import kamon.trace.TraceLocal import kamon.trace.TraceLocal.AvailableToMdc import org.scalatest.BeforeAndAfter @@ -34,7 +35,7 @@ import scala.concurrent.duration._ import scala.concurrent.{ Await, Future } class LoggerLikeInstrumentationSpec extends PlaySpec with OneServerPerSuite with BeforeAndAfter { - + Kamon.start() System.setProperty("config.file", "./kamon-play/src/test/resources/conf/application.conf") val executor = scala.concurrent.ExecutionContext.Implicits.global diff --git a/kamon-play/src/test/scala/kamon/play/RequestInstrumentationSpec.scala b/kamon-play/src/test/scala/kamon/play/RequestInstrumentationSpec.scala index 0feecb82..3e199df6 100644 --- a/kamon-play/src/test/scala/kamon/play/RequestInstrumentationSpec.scala +++ b/kamon-play/src/test/scala/kamon/play/RequestInstrumentationSpec.scala @@ -16,8 +16,6 @@ package kamon.play import kamon.Kamon -import kamon.http.HttpServerMetrics -import kamon.metric.{ Metrics, TraceMetrics } import kamon.metric.instrument.CollectionContext import kamon.play.action.TraceName import kamon.trace.TraceLocal.HttpContextKey @@ -33,13 +31,12 @@ import play.api.test.Helpers._ import play.api.test._ import play.core.Router.{ HandlerDef, Route, Routes } import play.core.{ DynamicPart, PathPattern, Router, StaticPart } -import play.libs.Akka import scala.concurrent.duration._ import scala.concurrent.{ Await, Future } class RequestInstrumentationSpec extends PlaySpec with OneServerPerSuite { - + Kamon.start() System.setProperty("config.file", "./kamon-play/src/test/resources/conf/application.conf") val executor = scala.concurrent.ExecutionContext.Implicits.global @@ -130,17 +127,17 @@ class RequestInstrumentationSpec extends PlaySpec with OneServerPerSuite { "response to the getRouted Action and normalise the current TraceContext name" in { Await.result(WS.url("http://localhost:19001/getRouted").get(), 10 seconds) - Kamon(Metrics)(Akka.system()).find("getRouted.get", "trace") must not be empty + Kamon.metrics.find("getRouted.get", "trace") must not be empty } "response to the postRouted Action and normalise the current TraceContext name" in { Await.result(WS.url("http://localhost:19001/postRouted").post("content"), 10 seconds) - Kamon(Metrics)(Akka.system()).find("postRouted.post", "trace") must not be empty + Kamon.metrics.find("postRouted.post", "trace") must not be empty } "response to the showRouted Action and normalise the current TraceContext name" in { Await.result(WS.url("http://localhost:19001/showRouted/2").get(), 10 seconds) - Kamon(Metrics)(Akka.system()).find("show.some.id.get", "trace") must not be empty + Kamon.metrics.find("show.some.id.get", "trace") must not be empty } "include HttpContext information for help to diagnose possible errors" in { @@ -155,7 +152,7 @@ class RequestInstrumentationSpec extends PlaySpec with OneServerPerSuite { "record http server metrics for all processed requests" in { val collectionContext = CollectionContext(100) - Kamon(Metrics)(Akka.system()).find("play-server", "http-server").get.collect(collectionContext) + Kamon.metrics.find("play-server", "http-server").get.collect(collectionContext) for (repetition ← 1 to 10) { Await.result(route(FakeRequest(GET, "/default").withHeaders(traceTokenHeader)).get, 10 seconds) @@ -169,7 +166,7 @@ class RequestInstrumentationSpec extends PlaySpec with OneServerPerSuite { Await.result(routeWithOnError(FakeRequest(GET, "/error").withHeaders(traceTokenHeader)).get, 10 seconds) } - val snapshot = Kamon(Metrics)(Akka.system()).find("play-server", "http-server").get.collect(collectionContext) + val snapshot = Kamon.metrics.find("play-server", "http-server").get.collect(collectionContext) snapshot.counter("GET: /default_200").get.count must be(10) snapshot.counter("GET: /notFound_404").get.count must be(5) snapshot.counter("GET: /error_500").get.count must be(5) diff --git a/kamon-play/src/test/scala/kamon/play/WSInstrumentationSpec.scala b/kamon-play/src/test/scala/kamon/play/WSInstrumentationSpec.scala index 795453d0..9e87da84 100644 --- a/kamon-play/src/test/scala/kamon/play/WSInstrumentationSpec.scala +++ b/kamon-play/src/test/scala/kamon/play/WSInstrumentationSpec.scala @@ -17,8 +17,8 @@ package kamon.play import kamon.Kamon -import kamon.metric.{ Metrics, EntitySnapshot, TraceMetrics } -import kamon.trace.{ Tracer, TraceContext, SegmentCategory } +import kamon.metric.{ EntitySnapshot, TraceMetrics } +import kamon.trace.{ TraceContext, SegmentCategory } import org.scalatest.{ Matchers, WordSpecLike } import org.scalatestplus.play.OneServerPerSuite import play.api.libs.ws.WS @@ -26,12 +26,12 @@ import play.api.mvc.Action import play.api.mvc.Results.Ok import play.api.test.Helpers._ import play.api.test._ -import play.libs.Akka import scala.concurrent.Await import scala.concurrent.duration._ class WSInstrumentationSpec extends WordSpecLike with Matchers with OneServerPerSuite { + Kamon.start() import kamon.metric.TraceMetricsSpec.SegmentSyntax System.setProperty("config.file", "./kamon-play/src/test/resources/conf/application.conf") @@ -66,11 +66,11 @@ class WSInstrumentationSpec extends WordSpecLike with Matchers with OneServerPer } def newContext(name: String): TraceContext = - Kamon(Tracer)(Akka.system).newContext(name) + Kamon.tracer.newContext(name) def takeSnapshotOf(traceName: String): EntitySnapshot = { - val recorder = Kamon(Metrics)(Akka.system()).register(TraceMetrics, traceName).get.recorder - val collectionContext = Kamon(Metrics)(Akka.system()).buildDefaultCollectionContext + val recorder = Kamon.metrics.register(TraceMetrics, traceName).get.recorder + val collectionContext = Kamon.metrics.buildDefaultCollectionContext recorder.collect(collectionContext) } diff --git a/kamon-playground/src/main/scala/test/SimpleRequestProcessor.scala b/kamon-playground/src/main/scala/test/SimpleRequestProcessor.scala index 67ab3c97..86f0c93e 100644 --- a/kamon-playground/src/main/scala/test/SimpleRequestProcessor.scala +++ b/kamon-playground/src/main/scala/test/SimpleRequestProcessor.scala @@ -38,7 +38,7 @@ object SimpleRequestProcessor extends App with SimpleRoutingApp with RequestBuil import scala.concurrent.duration._ implicit val system = ActorSystem("test") - val kamon = Kamon(system) + Kamon.start() import test.SimpleRequestProcessor.system.dispatcher val printer = system.actorOf(Props[PrintWhatever]) @@ -49,7 +49,7 @@ object SimpleRequestProcessor extends App with SimpleRoutingApp with RequestBuil implicit val timeout = Timeout(30 seconds) - val counter = Kamon(UserMetrics).counter("requests") + val counter = Kamon.userMetrics.counter("requests") val pipeline = sendReceive val replier = system.actorOf(Props[Replier].withRouter(RoundRobinPool(nrOfInstances = 4)), "replier") @@ -179,6 +179,6 @@ object PingPong extends App { def receive: Actor.Receive = { case "ping" ⇒ sender ! "pong" } })) - pinger.tell("pong", ponger) + //pinger.tell("pong", ponger) } diff --git a/kamon-spray/src/main/scala/kamon/spray/SprayExtension.scala b/kamon-spray/src/main/scala/kamon/spray/SprayExtension.scala index 3df8d972..ab0fe50b 100644 --- a/kamon-spray/src/main/scala/kamon/spray/SprayExtension.scala +++ b/kamon-spray/src/main/scala/kamon/spray/SprayExtension.scala @@ -21,7 +21,7 @@ import akka.actor import akka.event.{ Logging, LoggingAdapter } import kamon.Kamon import kamon.http.HttpServerMetrics -import kamon.metric.{ Entity, Metrics } +import kamon.metric.Entity import spray.http.HttpHeaders.Host import spray.http.HttpRequest @@ -46,11 +46,11 @@ class SprayExtensionImpl(system: ExtendedActorSystem) extends SprayExtension { val log = Logging(system, "SprayExtension") val httpServerMetrics = { - val metricsExtension = Metrics.get(system) + val metricsExtension = Kamon.metrics val factory = metricsExtension.instrumentFactory(HttpServerMetrics.category) val entity = Entity("spray-server", HttpServerMetrics.category) - Metrics.get(system).register(entity, new HttpServerMetrics(factory)).recorder + metricsExtension.register(entity, new HttpServerMetrics(factory)).recorder } def generateTraceName(request: HttpRequest): String = diff --git a/kamon-spray/src/main/scala/kamon/spray/instrumentation/ClientRequestInstrumentation.scala b/kamon-spray/src/main/scala/kamon/spray/instrumentation/ClientRequestInstrumentation.scala index fa9063ad..d1e9036d 100644 --- a/kamon-spray/src/main/scala/kamon/spray/instrumentation/ClientRequestInstrumentation.scala +++ b/kamon-spray/src/main/scala/kamon/spray/instrumentation/ClientRequestInstrumentation.scala @@ -16,6 +16,7 @@ package spray.can.client +import kamon.Kamon import org.aspectj.lang.annotation._ import org.aspectj.lang.ProceedingJoinPoint import spray.http._ @@ -47,7 +48,7 @@ class ClientRequestInstrumentation { requestContext.traceContext TraceContext.map { ctx ⇒ - val sprayExtension = ctx.lookupExtension(Spray) + val sprayExtension = Kamon.extension(Spray) if (sprayExtension.settings.clientInstrumentationLevel == ClientInstrumentationLevel.HostLevelAPI) { if (requestContext.segment.isEmpty) { @@ -112,7 +113,7 @@ class ClientRequestInstrumentation { (request: HttpRequest) ⇒ { TraceContext.map { ctx ⇒ - val sprayExtension = ctx.lookupExtension(Spray) + val sprayExtension = Kamon.extension(Spray) val segment = if (sprayExtension.settings.clientInstrumentationLevel == ClientInstrumentationLevel.RequestLevelAPI) ctx.startSegment(sprayExtension.generateRequestLevelApiSegmentName(request), SegmentCategory.HttpClient, Spray.SegmentLibraryName) @@ -139,7 +140,7 @@ class ClientRequestInstrumentation { def aroundIncludingDefaultHeadersAtHttpHostConnector(pjp: ProceedingJoinPoint, request: HttpMessage, defaultHeaders: List[HttpHeader]): Any = { val modifiedHeaders = TraceContext.map { ctx ⇒ - val sprayExtension = ctx.lookupExtension(Spray) + val sprayExtension = Kamon.extension(Spray) if (sprayExtension.settings.includeTraceTokenHeader) RawHeader(sprayExtension.settings.traceTokenHeaderName, ctx.token) :: defaultHeaders else diff --git a/kamon-spray/src/main/scala/kamon/spray/instrumentation/ServerRequestInstrumentation.scala b/kamon-spray/src/main/scala/kamon/spray/instrumentation/ServerRequestInstrumentation.scala index 73287132..bf20d167 100644 --- a/kamon-spray/src/main/scala/kamon/spray/instrumentation/ServerRequestInstrumentation.scala +++ b/kamon-spray/src/main/scala/kamon/spray/instrumentation/ServerRequestInstrumentation.scala @@ -39,9 +39,8 @@ class ServerRequestInstrumentation { @After("openRequestInit(openRequest, request)") def afterInit(openRequest: TraceContextAware, request: HttpRequest): Unit = { - val system: ActorSystem = openRequest.asInstanceOf[OpenRequest].context.actorContext.system - val tracer = Tracer.get(system) - val sprayExtension = Kamon(Spray)(system) + import Kamon.tracer + val sprayExtension = Kamon(Spray) val defaultTraceName = sprayExtension.generateTraceName(request) val token = if (sprayExtension.settings.includeTraceTokenHeader) { @@ -77,7 +76,7 @@ class ServerRequestInstrumentation { if (incomingContext.isEmpty) pjp.proceed() else { - val sprayExtension = incomingContext.lookupExtension(Spray) + val sprayExtension = Kamon(Spray) val proceedResult = if (sprayExtension.settings.includeTraceTokenHeader) { val responseWithHeader = includeTraceTokenIfPossible(response, sprayExtension.settings.traceTokenHeaderName, incomingContext.token) @@ -98,7 +97,7 @@ class ServerRequestInstrumentation { def verifyTraceContextConsistency(incomingTraceContext: TraceContext, storedTraceContext: TraceContext): Unit = { def publishWarning(text: String): Unit = - storedTraceContext.lookupExtension(Spray).log.warning(text) + Kamon(Spray).log.warning(text) if (incomingTraceContext.nonEmpty) { if (incomingTraceContext.token != storedTraceContext.token) diff --git a/kamon-spray/src/test/scala/kamon/spray/ClientRequestInstrumentationSpec.scala b/kamon-spray/src/test/scala/kamon/spray/ClientRequestInstrumentationSpec.scala index c5d7d992..a99bbdcc 100644 --- a/kamon-spray/src/test/scala/kamon/spray/ClientRequestInstrumentationSpec.scala +++ b/kamon-spray/src/test/scala/kamon/spray/ClientRequestInstrumentationSpec.scala @@ -264,14 +264,14 @@ class ClientRequestInstrumentationSpec extends BaseKamonSpec("client-request-ins def disableAutomaticTraceTokenPropagation(): Unit = setIncludeTraceToken(false) def setSegmentCollectionStrategy(strategy: ClientInstrumentationLevel.Level): Unit = { - val target = Kamon(Spray)(system).settings + val target = Kamon(Spray).settings val field = target.getClass.getDeclaredField("clientInstrumentationLevel") field.setAccessible(true) field.set(target, strategy) } def setIncludeTraceToken(include: Boolean): Unit = { - val target = Kamon(Spray)(system).settings + val target = Kamon(Spray).settings val field = target.getClass.getDeclaredField("includeTraceTokenHeader") field.setAccessible(true) field.set(target, include) diff --git a/kamon-spray/src/test/scala/kamon/spray/SprayServerTracingSpec.scala b/kamon-spray/src/test/scala/kamon/spray/SprayServerTracingSpec.scala index 1ae0cb98..bfd88ac8 100644 --- a/kamon-spray/src/test/scala/kamon/spray/SprayServerTracingSpec.scala +++ b/kamon-spray/src/test/scala/kamon/spray/SprayServerTracingSpec.scala @@ -79,7 +79,7 @@ class SprayServerTracingSpec extends BaseKamonSpec("spray-server-tracing-spec") def disableAutomaticTraceTokenPropagation(): Unit = setIncludeTraceToken(false) def setIncludeTraceToken(include: Boolean): Unit = { - val target = Kamon(Spray)(system).settings + val target = Kamon(Spray).settings val field = target.getClass.getDeclaredField("includeTraceTokenHeader") field.setAccessible(true) field.set(target, include) diff --git a/kamon-statsd/src/main/scala/kamon/statsd/StatsD.scala b/kamon-statsd/src/main/scala/kamon/statsd/StatsD.scala index d363bdd4..d406faf6 100644 --- a/kamon-statsd/src/main/scala/kamon/statsd/StatsD.scala +++ b/kamon-statsd/src/main/scala/kamon/statsd/StatsD.scala @@ -40,7 +40,7 @@ class StatsDExtension(system: ExtendedActorSystem) extends Kamon.Extension { private val config = system.settings.config private val statsDConfig = config.getConfig("kamon.statsd") - val metricsExtension = Kamon(Metrics) + val metricsExtension = Kamon.metrics val tickInterval = metricsExtension.settings.tickInterval val statsDHost = new InetSocketAddress(statsDConfig.getString("hostname"), statsDConfig.getInt("port")) diff --git a/kamon-statsd/src/test/scala/kamon/statsd/StatsDMetricSenderSpec.scala b/kamon-statsd/src/test/scala/kamon/statsd/StatsDMetricSenderSpec.scala index a0d787d9..0211ac0f 100644 --- a/kamon-statsd/src/test/scala/kamon/statsd/StatsDMetricSenderSpec.scala +++ b/kamon-statsd/src/test/scala/kamon/statsd/StatsDMetricSenderSpec.scala @@ -20,6 +20,7 @@ import akka.testkit.{ TestKitBase, TestProbe } import akka.actor.{ ActorRef, Props, ActorSystem } import kamon.Kamon import kamon.metric.instrument.{ InstrumentFactory, UnitOfMeasurement } +import kamon.testkit.BaseKamonSpec import kamon.util.MilliTimestamp import org.scalatest.{ Matchers, WordSpecLike } import kamon.metric._ @@ -28,28 +29,26 @@ import kamon.metric.SubscriptionsDispatcher.TickMetricSnapshot import java.net.InetSocketAddress import com.typesafe.config.ConfigFactory -class StatsDMetricSenderSpec extends TestKitBase with WordSpecLike with Matchers { - implicit lazy val system: ActorSystem = ActorSystem("statsd-metric-sender-spec", ConfigFactory.parseString( - """ - |kamon { - | statsd.simple-metric-key-generator { - | application = kamon - | hostname-override = kamon-host - | include-hostname = true - | metric-name-normalization-strategy = normalize - | } - |} - | - """.stripMargin)) +class StatsDMetricSenderSpec extends BaseKamonSpec("statsd-metric-sender-spec") { + override lazy val config = + ConfigFactory.parseString( + """ + |kamon { + | statsd.simple-metric-key-generator { + | application = kamon + | hostname-override = kamon-host + | include-hostname = true + | metric-name-normalization-strategy = normalize + | } + |} + | + """.stripMargin) implicit val metricKeyGenerator = new SimpleMetricKeyGenerator(system.settings.config) { override def hostName: String = "localhost_local" } - val collectionContext = Kamon(Metrics).buildDefaultCollectionContext - "the StatsDMetricSender" should { - "flush the metrics data after processing the tick, even if the max-packet-size is not reached" in new UdpListenerFixture { val testMetricKey = buildMetricKey(testEntity, "metric-one") val testRecorder = buildRecorder("user/kamon") @@ -134,7 +133,7 @@ class StatsDMetricSenderSpec extends TestKitBase with WordSpecLike with Matchers } def buildRecorder(name: String): TestEntityRecorder = { - Kamon(Metrics).register(TestEntityRecorder, name).get.recorder + Kamon.metrics.register(TestEntityRecorder, name).get.recorder } def setup(metrics: Map[Entity, EntitySnapshot]): TestProbe = { diff --git a/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsExtension.scala b/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsExtension.scala index 8a27dd03..ebdaf01f 100644 --- a/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsExtension.scala +++ b/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsExtension.scala @@ -42,7 +42,7 @@ class SystemMetricsExtension(system: ExtendedActorSystem) extends Kamon.Extensio val sigarFolder = config.getString("sigar-native-folder") val sigarRefreshInterval = config.getFiniteDuration("sigar-metrics-refresh-interval") val contextSwitchesRefreshInterval = config.getFiniteDuration("context-switches-refresh-interval") - val metricsExtension = Kamon(Metrics)(system) + val metricsExtension = Kamon.metrics // Sigar-based metrics SigarProvisioner.provision(new File(sigarFolder)) diff --git a/kamon-system-metrics/src/main/scala/kamon/system/custom/ContextSwitchesMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/system/custom/ContextSwitchesMetrics.scala index 0f1cffca..384c89f1 100644 --- a/kamon-system-metrics/src/main/scala/kamon/system/custom/ContextSwitchesMetrics.scala +++ b/kamon-system-metrics/src/main/scala/kamon/system/custom/ContextSwitchesMetrics.scala @@ -88,7 +88,7 @@ class ContextSwitchesMetrics(pid: Long, log: LoggingAdapter, instrumentFactory: object ContextSwitchesMetrics { def register(system: ActorSystem, refreshInterval: FiniteDuration): ContextSwitchesMetrics = { - val metricsExtension = Kamon(Metrics)(system) + val metricsExtension = Kamon.metrics val log = Logging(system, "ContextSwitchesMetrics") val pid = (new Sigar).getPid diff --git a/kamon-system-metrics/src/main/scala/kamon/system/sigar/SigarMetricsUpdater.scala b/kamon-system-metrics/src/main/scala/kamon/system/sigar/SigarMetricsUpdater.scala index 67eb48a9..e68b0ede 100644 --- a/kamon-system-metrics/src/main/scala/kamon/system/sigar/SigarMetricsUpdater.scala +++ b/kamon-system-metrics/src/main/scala/kamon/system/sigar/SigarMetricsUpdater.scala @@ -19,7 +19,7 @@ package kamon.system.sigar import akka.actor.{ Props, Actor } import kamon.Kamon import kamon.metric.instrument.InstrumentFactory -import kamon.metric.{ Entity, EntityRecorder, MetricsExtension, Metrics } +import kamon.metric.{ Entity, EntityRecorder, MetricsExtension } import kamon.system.sigar.SigarMetricsUpdater.UpdateSigarMetrics import org.hyperic.sigar.Sigar @@ -27,7 +27,7 @@ import scala.concurrent.duration.FiniteDuration class SigarMetricsUpdater(refreshInterval: FiniteDuration) extends Actor { val sigar = new Sigar - val metricsExtension = Kamon(Metrics)(context.system) + val metricsExtension = Kamon.metrics val sigarMetrics = List( CpuMetrics.register(sigar, metricsExtension), diff --git a/project/Projects.scala b/project/Projects.scala index 2004ac07..e2aed340 100644 --- a/project/Projects.scala +++ b/project/Projects.scala @@ -111,7 +111,7 @@ object Projects extends Build { compile(sprayCan, sprayClient, sprayRouting, sprayJson, sprayJsonLenses, newrelic, akkaSlf4j) ++ provided(aspectJ) ++ test(scalatest, akkaTestKit, sprayTestkit, slf4Api, akkaSlf4j)) - .dependsOn(kamonCore) + .dependsOn(kamonCore % "compile->compile;test->test") .dependsOn(kamonTestkit % "compile->compile;test->test") @@ -165,7 +165,7 @@ object Projects extends Build { libraryDependencies ++= compile(akkaActor) ++ test(scalatest, akkaTestKit, slf4Api, slf4nop)) - .dependsOn(kamonCore) + .dependsOn(kamonCore % "compile->compile;test->test") .dependsOn(kamonSystemMetrics % "provided") lazy val kamonDatadog = Project("kamon-datadog", file("kamon-datadog")) @@ -175,7 +175,7 @@ object Projects extends Build { libraryDependencies ++= compile(akkaActor) ++ test(scalatest, akkaTestKit, slf4Api, slf4nop)) - .dependsOn(kamonCore) + .dependsOn(kamonCore % "compile->compile;test->test") .dependsOn(kamonSystemMetrics % "provided") lazy val kamonLogReporter = Project("kamon-log-reporter", file("kamon-log-reporter")) diff --git a/project/Settings.scala b/project/Settings.scala index 22a1f4e7..700312c1 100644 --- a/project/Settings.scala +++ b/project/Settings.scala @@ -13,9 +13,11 @@ * ========================================================================================= */ +import com.typesafe.sbt.SbtAspectj.AspectjKeys._ +import sbt.Tests.{SubProcess, Group} import sbt._ import Keys._ -import com.typesafe.sbt.SbtScalariform +import com.typesafe.sbt.{SbtAspectj, SbtScalariform} import com.typesafe.sbt.SbtScalariform.ScalariformKeys import Publish.{settings => publishSettings} import Release.{settings => releaseSettings} @@ -29,17 +31,18 @@ object Settings { val ScalaVersion = "2.10.4" lazy val basicSettings = Seq( - scalaVersion := ScalaVersion, - resolvers ++= Dependencies.resolutionRepos, - fork in run := true, + scalaVersion := ScalaVersion, + resolvers ++= Dependencies.resolutionRepos, + fork in run := true, + testGrouping in Test := singleTests((definedTests in Test).value, (javaOptions in Test).value), javacOptions in compile := Seq( "-Xlint:-options", "-source", JavaVersion, "-target", JavaVersion ), - javacOptions in doc := Seq( + javacOptions in doc := Seq( "-source", JavaVersion ), - scalacOptions := Seq( + scalacOptions := Seq( "-encoding", "utf8", "-g:vars", @@ -54,6 +57,15 @@ object Settings { "-Xlog-reflective-calls" )) ++ publishSettings ++ releaseSettings ++ graphSettings + + def singleTests(tests: Seq[TestDefinition], jvmSettings: Seq[String]): Seq[Group] = + tests map { test => + new Group( + name = test.name, + tests = Seq(test), + runPolicy = SubProcess(ForkOptions(runJVMOptions = jvmSettings))) + } + lazy val formatSettings = SbtScalariform.scalariformSettings ++ Seq( ScalariformKeys.preferences in Compile := formattingPreferences, ScalariformKeys.preferences in Test := formattingPreferences |