aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--README.md2
-rw-r--r--kamon-akka/src/main/resources/reference.conf17
-rw-r--r--kamon-akka/src/main/scala/kamon/akka/DispatcherMetrics.scala40
-rw-r--r--kamon-akka/src/main/scala/kamon/akka/instrumentation/ActorCellInstrumentation.scala41
-rw-r--r--kamon-akka/src/main/scala/kamon/akka/instrumentation/DispatcherInstrumentation.scala22
-rw-r--r--kamon-akka/src/test/scala/kamon/akka/ActorMetricsSpec.scala14
-rw-r--r--kamon-akka/src/test/scala/kamon/akka/DispatcherMetricsSpec.scala49
-rw-r--r--kamon-akka/src/test/scala/kamon/akka/RouterMetricsSpec.scala10
-rw-r--r--kamon-akka/src/test/scala/kamon/akka/instrumentation/ActorLoggingInstrumentationSpec.scala2
-rw-r--r--kamon-akka/src/test/scala/kamon/akka/instrumentation/ActorSystemMessageInstrumentationSpec.scala2
-rw-r--r--kamon-akka/src/test/scala/kamon/akka/instrumentation/AskPatternInstrumentationSpec.scala2
-rw-r--r--kamon-annotation/src/main/java/kamon/annotation/Count.java69
-rw-r--r--kamon-annotation/src/main/java/kamon/annotation/EnableKamon.java27
-rw-r--r--kamon-annotation/src/main/java/kamon/annotation/Histogram.java89
-rw-r--r--kamon-annotation/src/main/java/kamon/annotation/MinMaxCount.java71
-rw-r--r--kamon-annotation/src/main/java/kamon/annotation/Segment.java78
-rw-r--r--kamon-annotation/src/main/java/kamon/annotation/Time.java57
-rw-r--r--kamon-annotation/src/main/java/kamon/annotation/Trace.java69
-rw-r--r--kamon-annotation/src/main/java/kamon/annotation/el/resolver/PrivateFieldELResolver.java117
-rw-r--r--kamon-annotation/src/main/resources/META-INF/aop.xml18
-rw-r--r--kamon-annotation/src/main/resources/reference.conf19
-rw-r--r--kamon-annotation/src/main/scala/kamon/annotation/Annotation.scala34
-rw-r--r--kamon-annotation/src/main/scala/kamon/annotation/el/ELProcessorFactory.scala (renamed from kamon-core/src/main/scala/kamon/instrumentation/AspectJWeaverMissingWarning.scala)29
-rw-r--r--kamon-annotation/src/main/scala/kamon/annotation/el/EnhancedELProcessor.scala60
-rw-r--r--kamon-annotation/src/main/scala/kamon/annotation/instrumentation/AnnotationInstrumentation.scala81
-rw-r--r--kamon-annotation/src/main/scala/kamon/annotation/instrumentation/BaseAnnotationInstrumentation.scala178
-rw-r--r--kamon-annotation/src/main/scala/kamon/annotation/instrumentation/StaticAnnotationInstrumentation.scala100
-rw-r--r--kamon-annotation/src/test/java/kamon/annotation/AnnotatedJavaClass.java66
-rw-r--r--kamon-annotation/src/test/resources/logback.xml12
-rw-r--r--kamon-annotation/src/test/scala/kamon/annotation/AnnotationInstrumentationSpec.scala199
-rw-r--r--kamon-annotation/src/test/scala/kamon/annotation/StaticAnnotationInstrumentationJavaSpec.scala130
-rw-r--r--kamon-annotation/src/test/scala/kamon/annotation/StaticAnnotationInstrumentationSpec.scala188
-rw-r--r--kamon-core/src/main/resources/META-INF/aop.xml2
-rw-r--r--kamon-core/src/main/scala/kamon/Kamon.scala13
-rw-r--r--kamon-core/src/main/scala/kamon/ModuleLoader.scala123
-rw-r--r--kamon-core/src/main/scala/kamon/metric/Entity.scala27
-rw-r--r--kamon-core/src/main/scala/kamon/metric/EntityRecorder.scala103
-rw-r--r--kamon-core/src/main/scala/kamon/metric/MetricKey.scala134
-rw-r--r--kamon-core/src/main/scala/kamon/metric/Metrics.scala279
-rw-r--r--kamon-core/src/main/scala/kamon/metric/SimpleMetrics.scala204
-rw-r--r--kamon-core/src/main/scala/kamon/metric/TraceMetrics.scala33
-rw-r--r--kamon-core/src/main/scala/kamon/metric/instrument/Gauge.scala4
-rw-r--r--kamon-core/src/main/scala/kamon/metric/instrument/Instrument.scala16
-rw-r--r--kamon-core/src/main/scala/kamon/metric/instrument/RefreshScheduler.scala16
-rw-r--r--kamon-core/src/main/scala/kamon/metric/instrument/UnitOfMeasurement.scala28
-rw-r--r--kamon-core/src/main/scala/kamon/supervisor/ModuleSupervisorExtension.scala125
-rw-r--r--kamon-core/src/main/scala/kamon/supervisor/ModuleSupervisorSettings.scala49
-rw-r--r--kamon-core/src/main/scala/kamon/trace/MetricsOnlyContext.scala26
-rw-r--r--kamon-core/src/main/scala/kamon/trace/Tracer.scala4
-rw-r--r--kamon-core/src/main/scala/kamon/trace/TracingContext.scala5
-rw-r--r--kamon-core/src/main/scala/kamon/util/FunctionalInterfaces.scala (renamed from kamon-core/src/main/scala/kamon/supervisor/AspectJPresent.scala)20
-rw-r--r--kamon-core/src/main/scala/kamon/util/JavaTags.scala14
-rw-r--r--kamon-core/src/main/scala/kamon/util/Latency.scala29
-rw-r--r--kamon-core/src/main/scala/kamon/util/LazyActorRef.scala16
-rw-r--r--kamon-core/src/main/scala/kamon/util/MapMerge.scala2
-rw-r--r--kamon-core/src/main/scala/kamon/util/SameThreadExecutionContext.scala30
-rw-r--r--kamon-core/src/test/scala/kamon/metric/SimpleMetricsSpec.scala74
-rw-r--r--kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala20
-rw-r--r--kamon-core/src/test/scala/kamon/metric/TickMetricSnapshotBufferSpec.scala14
-rw-r--r--kamon-core/src/test/scala/kamon/metric/TraceMetricsSpec.scala42
-rw-r--r--kamon-core/src/test/scala/kamon/testkit/BaseKamonSpec.scala7
-rw-r--r--kamon-datadog/src/main/scala/kamon/datadog/DatadogMetricsSender.scala15
-rw-r--r--kamon-datadog/src/test/scala/kamon/datadog/DatadogMetricSenderSpec.scala22
-rw-r--r--kamon-examples/kamon-play-example/app/Global.scala8
-rw-r--r--kamon-examples/kamon-play-example/app/controllers/KamonPlayExample.scala3
-rw-r--r--kamon-examples/kamon-play-example/app/filters/TraceLocalFilter.scala2
-rw-r--r--kamon-examples/kamon-play-example/conf/application.conf2
-rw-r--r--kamon-examples/kamon-play-example/project/Build.scala4
-rw-r--r--kamon-examples/kamon-play-example/project/plugins.sbt2
-rw-r--r--kamon-jdbc/src/main/scala/kamon/jdbc/instrumentation/StatementInstrumentation.scala17
-rw-r--r--kamon-jdbc/src/test/scala/kamon/jdbc/instrumentation/StatementInstrumentationSpec.scala42
-rw-r--r--kamon-log-reporter/src/main/scala/kamon/logreporter/LogReporter.scala183
-rw-r--r--kamon-newrelic/src/main/scala/kamon/newrelic/CustomMetricExtractor.scala17
-rw-r--r--kamon-newrelic/src/main/scala/kamon/newrelic/Metric.scala4
-rw-r--r--kamon-newrelic/src/main/scala/kamon/newrelic/WebTransactionMetricExtractor.scala51
-rw-r--r--kamon-newrelic/src/test/scala/kamon/newrelic/MetricReporterSpec.scala14
-rw-r--r--kamon-play/src/main/scala/kamon/play/Play.scala8
-rw-r--r--kamon-play/src/test/scala/kamon/play/WSInstrumentationSpec.scala38
-rw-r--r--kamon-playground/src/main/resources/application.conf9
-rw-r--r--kamon-playground/src/main/scala/test/SimpleRequestProcessor.scala4
-rw-r--r--kamon-spray/src/main/scala/kamon/spray/SprayExtension.scala5
-rw-r--r--kamon-spray/src/test/scala/kamon/spray/ClientRequestInstrumentationSpec.scala32
-rw-r--r--kamon-statsd/src/main/resources/reference.conf15
-rw-r--r--kamon-statsd/src/main/scala/kamon/statsd/SimpleMetricKeyGenerator.scala15
-rw-r--r--kamon-statsd/src/main/scala/kamon/statsd/StatsD.scala4
-rw-r--r--kamon-statsd/src/main/scala/kamon/statsd/StatsDMetricsSender.scala10
-rw-r--r--kamon-statsd/src/test/scala/kamon/statsd/SimpleMetricKeyGeneratorSpec.scala2
-rw-r--r--kamon-statsd/src/test/scala/kamon/statsd/StatsDMetricSenderSpec.scala6
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsExtension.scala35
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/custom/ContextSwitchesMetrics.scala4
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/jmx/GarbageCollectionMetrics.scala7
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/jmx/JmxSystemMetricRecorderCompanion.scala8
-rw-r--r--kamon-system-metrics/src/main/scala/kamon/system/sigar/SigarMetricsUpdater.scala8
-rw-r--r--project/Dependencies.scala66
-rw-r--r--project/Projects.scala17
-rw-r--r--project/Publish.scala2
-rw-r--r--project/Settings.scala15
-rw-r--r--project/plugins.sbt2
99 files changed, 2954 insertions, 1097 deletions
diff --git a/.travis.yml b/.travis.yml
index f49aa1a1..1285f35d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,7 +5,7 @@ script:
scala:
- 2.11.5
jdk:
- - oraclejdk8
+ - oraclejdk8
before_script:
- mkdir $TRAVIS_BUILD_DIR/tmp
- export SBT_OPTS="-Djava.io.tmpdir=$TRAVIS_BUILD_DIR/tmp"
diff --git a/README.md b/README.md
index 0f6d2ecf..b6c806ca 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@ Kamon [![Build Status](https://api.travis-ci.org/kamon-io/Kamon.png)](https:
Kamon is a set of tools that will help you monitor your reactive applications.
-
+[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/kamon-io/Kamon?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
![First Implementation of Kamon in Action!!!](kamon-dashboard/kamon-dashboard-screenshot.png)
diff --git a/kamon-akka/src/main/resources/reference.conf b/kamon-akka/src/main/resources/reference.conf
index cc2b6060..c1e59e63 100644
--- a/kamon-akka/src/main/resources/reference.conf
+++ b/kamon-akka/src/main/resources/reference.conf
@@ -15,10 +15,21 @@ kamon {
}
metric.filters {
- actor.includes = []
- actor.excludes = [ "", "user", "system**", "user/IO-**" ]
- }
+ akka-actor {
+ includes = []
+ excludes = [ "*/system/**", "*/user/IO-**" ]
+ }
+
+ akka-router {
+ includes = []
+ excludes = []
+ }
+ akka-dispatcher {
+ includes = []
+ excludes = []
+ }
+ }
modules {
kamon-akka {
diff --git a/kamon-akka/src/main/scala/kamon/akka/DispatcherMetrics.scala b/kamon-akka/src/main/scala/kamon/akka/DispatcherMetrics.scala
index acf92e70..23ecde79 100644
--- a/kamon-akka/src/main/scala/kamon/akka/DispatcherMetrics.scala
+++ b/kamon-akka/src/main/scala/kamon/akka/DispatcherMetrics.scala
@@ -26,21 +26,10 @@ class ForkJoinPoolDispatcherMetrics(fjp: AkkaForkJoinPool, instrumentFactory: In
val paralellism = minMaxCounter("parallelism")
paralellism.increment(fjp.getParallelism) // Steady value.
- val poolSize = gauge("pool-size", () ⇒ {
- fjp.getPoolSize.toLong
- })
-
- val activeThreads = gauge("active-threads", () ⇒ {
- fjp.getActiveThreadCount.toLong
- })
-
- val runningThreads = gauge("running-threads", () ⇒ {
- fjp.getRunningThreadCount.toLong
- })
-
- val queuedTaskCount = gauge("queued-task-count", () ⇒ {
- fjp.getQueuedTaskCount
- })
+ val poolSize = gauge("pool-size", fjp.getPoolSize.toLong)
+ val activeThreads = gauge("active-threads", fjp.getActiveThreadCount.toLong)
+ val runningThreads = gauge("running-threads", fjp.getRunningThreadCount.toLong)
+ val queuedTaskCount = gauge("queued-task-count", fjp.getQueuedTaskCount)
}
object ForkJoinPoolDispatcherMetrics {
@@ -52,29 +41,16 @@ object ForkJoinPoolDispatcherMetrics {
}
class ThreadPoolExecutorDispatcherMetrics(tpe: ThreadPoolExecutor, instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) {
- val corePoolSize = gauge("core-pool-size", () ⇒ {
- tpe.getCorePoolSize.toLong
- })
-
- val maxPoolSize = gauge("max-pool-size", () ⇒ {
- tpe.getMaximumPoolSize.toLong
- })
-
- val poolSize = gauge("pool-size", () ⇒ {
- tpe.getPoolSize.toLong
- })
-
- val activeThreads = gauge("active-threads", () ⇒ {
- tpe.getActiveCount.toLong
- })
-
+ val corePoolSize = gauge("core-pool-size", tpe.getCorePoolSize.toLong)
+ val maxPoolSize = gauge("max-pool-size", tpe.getMaximumPoolSize.toLong)
+ val poolSize = gauge("pool-size", tpe.getPoolSize.toLong)
+ val activeThreads = gauge("active-threads", tpe.getActiveCount.toLong)
val processedTasks = gauge("processed-tasks", DifferentialValueCollector(() ⇒ {
tpe.getTaskCount
}))
}
object ThreadPoolExecutorDispatcherMetrics {
-
def factory(tpe: ThreadPoolExecutor) = new EntityRecorderFactory[ThreadPoolExecutorDispatcherMetrics] {
def category: String = AkkaDispatcherMetrics.Category
def createRecorder(instrumentFactory: InstrumentFactory) = new ThreadPoolExecutorDispatcherMetrics(tpe, instrumentFactory)
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 4783484f..deb43499 100644
--- a/kamon-akka/src/main/scala/kamon/akka/instrumentation/ActorCellInstrumentation.scala
+++ b/kamon-akka/src/main/scala/kamon/akka/instrumentation/ActorCellInstrumentation.scala
@@ -34,13 +34,18 @@ 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 = {
- Kamon.metrics.register(ActorMetrics, ref.path.elements.mkString("/")).map { registration ⇒
+ def isRootSupervisor(path: String): Boolean = path.length == 0 || path == "user" || path == "system"
+
+ val pathString = ref.path.elements.mkString("/")
+ val actorEntity = Entity(system.name + "/" + pathString, ActorMetrics.category)
+
+ if (!isRootSupervisor(pathString) && Kamon.metrics.shouldTrack(actorEntity)) {
+ val actorMetricsRecorder = Kamon.metrics.entity(ActorMetrics, actorEntity)
val cellMetrics = cell.asInstanceOf[ActorCellMetrics]
- cellMetrics.entity = registration.entity
- cellMetrics.recorder = Some(registration.recorder)
+ cellMetrics.entity = actorEntity
+ cellMetrics.recorder = Some(actorMetricsRecorder)
}
-
}
@Pointcut("execution(* akka.actor.ActorCell.invoke(*)) && this(cell) && args(envelope)")
@@ -57,19 +62,19 @@ class ActorCellInstrumentation {
pjp.proceed()
}
} finally {
- cellMetrics.recorder.map { am ⇒
- val processingTime = System.nanoTime() - timestampBeforeProcessing
- val timeInMailbox = timestampBeforeProcessing - contextAndTimestamp.captureNanoTime
+ val processingTime = System.nanoTime() - timestampBeforeProcessing
+ val timeInMailbox = timestampBeforeProcessing - contextAndTimestamp.captureNanoTime
+ cellMetrics.recorder.map { am ⇒
am.processingTime.record(processingTime)
am.timeInMailbox.record(timeInMailbox)
am.mailboxSize.decrement()
+ }
- // In case that this actor is behind a router, record the metrics for the router.
- envelope.asInstanceOf[RouterAwareEnvelope].routerMetricsRecorder.map { rm ⇒
- rm.processingTime.record(processingTime)
- rm.timeInMailbox.record(timeInMailbox)
- }
+ // In case that this actor is behind a router, record the metrics for the router.
+ envelope.asInstanceOf[RouterAwareEnvelope].routerMetricsRecorder.map { rm ⇒
+ rm.processingTime.record(processingTime)
+ rm.timeInMailbox.record(timeInMailbox)
}
}
}
@@ -90,14 +95,14 @@ class ActorCellInstrumentation {
def afterStop(cell: ActorCell): Unit = {
val cellMetrics = cell.asInstanceOf[ActorCellMetrics]
cellMetrics.recorder.map { _ ⇒
- Kamon.metrics.unregister(cellMetrics.entity)
+ Kamon.metrics.removeEntity(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 { _ ⇒
- Kamon.metrics.unregister(routedCellMetrics.routerEntity)
+ Kamon.metrics.removeEntity(routedCellMetrics.routerEntity)
}
}
}
@@ -124,11 +129,13 @@ 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 = {
- Kamon.metrics.register(RouterMetrics, ref.path.elements.mkString("/")).map { registration ⇒
+ val routerEntity = Entity(system.name + "/" + ref.path.elements.mkString("/"), RouterMetrics.category)
+
+ if (Kamon.metrics.shouldTrack(routerEntity)) {
val cellMetrics = cell.asInstanceOf[RoutedActorCellMetrics]
- cellMetrics.routerEntity = registration.entity
- cellMetrics.routerRecorder = Some(registration.recorder)
+ cellMetrics.routerEntity = routerEntity
+ cellMetrics.routerRecorder = Some(Kamon.metrics.entity(RouterMetrics, routerEntity))
}
}
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 7b15c443..931d8a45 100644
--- a/kamon-akka/src/main/scala/kamon/akka/instrumentation/DispatcherInstrumentation.scala
+++ b/kamon-akka/src/main/scala/kamon/akka/instrumentation/DispatcherInstrumentation.scala
@@ -59,10 +59,16 @@ class DispatcherInstrumentation {
private def registerDispatcher(dispatcherName: String, executorService: ExecutorService, system: ActorSystem): Unit =
executorService match {
case fjp: AkkaForkJoinPool ⇒
- Kamon.metrics.register(ForkJoinPoolDispatcherMetrics.factory(fjp), dispatcherName)
+ val dispatcherEntity = Entity(system.name + "/" + dispatcherName, AkkaDispatcherMetrics.Category, tags = Map("dispatcher-type" -> "fork-join-pool"))
+
+ if (Kamon.metrics.shouldTrack(dispatcherEntity))
+ Kamon.metrics.entity(ForkJoinPoolDispatcherMetrics.factory(fjp), dispatcherEntity)
case tpe: ThreadPoolExecutor ⇒
- Kamon.metrics.register(ThreadPoolExecutorDispatcherMetrics.factory(tpe), dispatcherName)
+ val dispatcherEntity = Entity(system.name + "/" + dispatcherName, AkkaDispatcherMetrics.Category, tags = Map("dispatcher-type" -> "thread-pool-executor"))
+
+ if (Kamon.metrics.shouldTrack(dispatcherEntity))
+ Kamon.metrics.entity(ThreadPoolExecutorDispatcherMetrics.factory(tpe), dispatcherEntity)
case others ⇒ // Currently not interested in other kinds of dispatchers.
}
@@ -120,7 +126,17 @@ class DispatcherInstrumentation {
import lazyExecutor.lookupData
if (lookupData.actorSystem != null)
- Kamon.metrics.unregister(Entity(lookupData.dispatcherName, AkkaDispatcherMetrics.Category))
+ lazyExecutor.asInstanceOf[ExecutorServiceDelegate].executor match {
+ case fjp: AkkaForkJoinPool ⇒
+ Kamon.metrics.removeEntity(Entity(lookupData.actorSystem.name + "/" + lookupData.dispatcherName,
+ AkkaDispatcherMetrics.Category, tags = Map("dispatcher-type" -> "fork-join-pool")))
+
+ case tpe: ThreadPoolExecutor ⇒
+ Kamon.metrics.removeEntity(Entity(lookupData.actorSystem.name + "/" + lookupData.dispatcherName,
+ AkkaDispatcherMetrics.Category, tags = Map("dispatcher-type" -> "thread-pool-executor")))
+
+ case other ⇒ // nothing to remove.
+ }
}
}
diff --git a/kamon-akka/src/test/scala/kamon/akka/ActorMetricsSpec.scala b/kamon-akka/src/test/scala/kamon/akka/ActorMetricsSpec.scala
index 19a71053..4647abc0 100644
--- a/kamon-akka/src/test/scala/kamon/akka/ActorMetricsSpec.scala
+++ b/kamon-akka/src/test/scala/kamon/akka/ActorMetricsSpec.scala
@@ -38,8 +38,8 @@ class ActorMetricsSpec extends BaseKamonSpec("actor-metrics-spec") {
|
| filters {
| akka-actor {
- | includes = [ "user/tracked-*", "user/measuring-*", "user/clean-after-collect", "user/stop" ]
- | excludes = [ "user/tracked-explicitly-excluded", "user/non-tracked-actor" ]
+ | includes = [ "*/user/tracked-*", "*/user/measuring-*", "*/user/clean-after-collect", "*/user/stop", "*/" ]
+ | excludes = [ "*/user/tracked-explicitly-excluded", "*/user/non-tracked-actor" ]
| }
| }
|
@@ -64,6 +64,10 @@ class ActorMetricsSpec extends BaseKamonSpec("actor-metrics-spec") {
actorMetricsRecorderOf(trackedButExplicitlyExcluded) shouldBe empty
}
+ "not pick up the root supervisor" in {
+ Kamon.metrics.find("actor-metrics-spec/", ActorMetrics.category) shouldBe empty
+ }
+
"reset all recording instruments after taking a snapshot" in new ActorMetricsFixtures {
val trackedActor = createTestActor("clean-after-collect")
@@ -151,7 +155,7 @@ class ActorMetricsSpec extends BaseKamonSpec("actor-metrics-spec") {
trackedActor ! PoisonPill
deathWatcher.expectTerminated(trackedActor)
- actorMetricsRecorderOf(trackedActor).get shouldNot be theSameInstanceAs (firstRecorder)
+ actorMetricsRecorderOf(trackedActor) shouldBe empty
}
}
@@ -162,10 +166,10 @@ class ActorMetricsSpec extends BaseKamonSpec("actor-metrics-spec") {
val buffer: LongBuffer = LongBuffer.allocate(10000)
}
- def actorRecorderName(ref: ActorRef): String = ref.path.elements.mkString("/")
+ def actorRecorderName(ref: ActorRef): String = system.name + "/" + ref.path.elements.mkString("/")
def actorMetricsRecorderOf(ref: ActorRef): Option[ActorMetrics] =
- Kamon.metrics.register(ActorMetrics, actorRecorderName(ref)).map(_.recorder)
+ Kamon.metrics.find(actorRecorderName(ref), ActorMetrics.category).map(_.asInstanceOf[ActorMetrics])
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/akka/DispatcherMetricsSpec.scala b/kamon-akka/src/test/scala/kamon/akka/DispatcherMetricsSpec.scala
index 2379bff5..02a5a0d4 100644
--- a/kamon-akka/src/test/scala/kamon/akka/DispatcherMetricsSpec.scala
+++ b/kamon-akka/src/test/scala/kamon/akka/DispatcherMetricsSpec.scala
@@ -15,7 +15,6 @@
package kamon.akka
-
import akka.actor.ActorRef
import akka.dispatch.MessageDispatcher
import akka.testkit.TestProbe
@@ -37,8 +36,8 @@ class DispatcherMetricsSpec extends BaseKamonSpec("dispatcher-metrics-spec") {
|
| filters = {
| akka-dispatcher {
- | includes = [ "*" ]
- | excludes = [ "explicitly-excluded" ]
+ | includes = [ "**" ]
+ | excludes = [ "*/explicitly-excluded" ]
| }
| }
|
@@ -85,16 +84,16 @@ class DispatcherMetricsSpec extends BaseKamonSpec("dispatcher-metrics-spec") {
val tpeDispatcher = forceInit(system.dispatchers.lookup("tracked-tpe"))
val excludedDispatcher = forceInit(system.dispatchers.lookup("explicitly-excluded"))
- findDispatcherRecorder(defaultDispatcher) shouldNot be(empty)
- findDispatcherRecorder(fjpDispatcher) shouldNot be(empty)
- findDispatcherRecorder(tpeDispatcher) shouldNot be(empty)
- findDispatcherRecorder(excludedDispatcher) should be(empty)
+ findDispatcherRecorder(defaultDispatcher, "fork-join-pool") shouldNot be(empty)
+ findDispatcherRecorder(fjpDispatcher, "fork-join-pool") shouldNot be(empty)
+ findDispatcherRecorder(tpeDispatcher, "thread-pool-executor") shouldNot be(empty)
+ findDispatcherRecorder(excludedDispatcher, "fork-join-pool") should be(empty)
}
"record metrics for a dispatcher with thread-pool-executor" in {
implicit val tpeDispatcher = system.dispatchers.lookup("tracked-tpe")
- refreshDispatcherInstruments(tpeDispatcher)
- collectDispatcherMetrics(tpeDispatcher)
+ refreshDispatcherInstruments(tpeDispatcher, "thread-pool-executor")
+ collectDispatcherMetrics(tpeDispatcher, "thread-pool-executor")
Await.result({
Future.sequence {
@@ -102,8 +101,8 @@ class DispatcherMetricsSpec extends BaseKamonSpec("dispatcher-metrics-spec") {
}
}, 5 seconds)
- refreshDispatcherInstruments(tpeDispatcher)
- val snapshot = collectDispatcherMetrics(tpeDispatcher)
+ refreshDispatcherInstruments(tpeDispatcher, "thread-pool-executor")
+ val snapshot = collectDispatcherMetrics(tpeDispatcher, "thread-pool-executor")
snapshot.gauge("active-threads") should not be empty
snapshot.gauge("pool-size").get.min should be >= 7L
@@ -113,13 +112,13 @@ class DispatcherMetricsSpec extends BaseKamonSpec("dispatcher-metrics-spec") {
snapshot.gauge("processed-tasks").get.max should be(102L +- 5L)
// The processed tasks should be reset to 0 if no more tasks are submitted.
- val secondSnapshot = collectDispatcherMetrics(tpeDispatcher)
+ val secondSnapshot = collectDispatcherMetrics(tpeDispatcher, "thread-pool-executor")
secondSnapshot.gauge("processed-tasks").get.max should be(0)
}
"record metrics for a dispatcher with fork-join-executor" in {
implicit val fjpDispatcher = system.dispatchers.lookup("tracked-fjp")
- collectDispatcherMetrics(fjpDispatcher)
+ collectDispatcherMetrics(fjpDispatcher, "fork-join-pool")
Await.result({
Future.sequence {
@@ -127,8 +126,8 @@ class DispatcherMetricsSpec extends BaseKamonSpec("dispatcher-metrics-spec") {
}
}, 5 seconds)
- refreshDispatcherInstruments(fjpDispatcher)
- val snapshot = collectDispatcherMetrics(fjpDispatcher)
+ refreshDispatcherInstruments(fjpDispatcher, "fork-join-pool")
+ val snapshot = collectDispatcherMetrics(fjpDispatcher, "fork-join-pool")
snapshot.minMaxCounter("parallelism").get.max should be(22)
snapshot.gauge("pool-size").get.min should be >= 0L
@@ -143,28 +142,28 @@ class DispatcherMetricsSpec extends BaseKamonSpec("dispatcher-metrics-spec") {
implicit val tpeDispatcher = system.dispatchers.lookup("tracked-tpe")
implicit val fjpDispatcher = system.dispatchers.lookup("tracked-fjp")
- findDispatcherRecorder(fjpDispatcher) shouldNot be(empty)
- findDispatcherRecorder(tpeDispatcher) shouldNot be(empty)
+ findDispatcherRecorder(fjpDispatcher, "fork-join-pool") shouldNot be(empty)
+ findDispatcherRecorder(tpeDispatcher, "thread-pool-executor") shouldNot be(empty)
shutdownDispatcher(tpeDispatcher)
shutdownDispatcher(fjpDispatcher)
- findDispatcherRecorder(fjpDispatcher) should be(empty)
- findDispatcherRecorder(tpeDispatcher) should be(empty)
+ findDispatcherRecorder(fjpDispatcher, "fork-join-pool") should be(empty)
+ findDispatcherRecorder(tpeDispatcher, "thread-pool-executor") should be(empty)
}
}
def actorRecorderName(ref: ActorRef): String = ref.path.elements.mkString("/")
- def findDispatcherRecorder(dispatcher: MessageDispatcher): Option[EntityRecorder] =
- Kamon.metrics.find(dispatcher.id, "akka-dispatcher")
+ def findDispatcherRecorder(dispatcher: MessageDispatcher, dispatcherType: String): Option[EntityRecorder] =
+ Kamon.metrics.find(system.name + "/" + dispatcher.id, "akka-dispatcher", tags = Map("dispatcher-type" -> dispatcherType))
- def collectDispatcherMetrics(dispatcher: MessageDispatcher): EntitySnapshot =
- findDispatcherRecorder(dispatcher).map(_.collect(collectionContext)).get
+ def collectDispatcherMetrics(dispatcher: MessageDispatcher, dispatcherType: String): EntitySnapshot =
+ findDispatcherRecorder(dispatcher, dispatcherType).map(_.collect(collectionContext)).get
- def refreshDispatcherInstruments(dispatcher: MessageDispatcher): Unit = {
- findDispatcherRecorder(dispatcher) match {
+ def refreshDispatcherInstruments(dispatcher: MessageDispatcher, dispatcherType: String): Unit = {
+ findDispatcherRecorder(dispatcher, dispatcherType) match {
case Some(tpe: ThreadPoolExecutorDispatcherMetrics) ⇒
tpe.processedTasks.refreshValue()
tpe.activeThreads.refreshValue()
diff --git a/kamon-akka/src/test/scala/kamon/akka/RouterMetricsSpec.scala b/kamon-akka/src/test/scala/kamon/akka/RouterMetricsSpec.scala
index ec55648b..4128c9ef 100644
--- a/kamon-akka/src/test/scala/kamon/akka/RouterMetricsSpec.scala
+++ b/kamon-akka/src/test/scala/kamon/akka/RouterMetricsSpec.scala
@@ -39,8 +39,8 @@ class RouterMetricsSpec extends BaseKamonSpec("router-metrics-spec") {
|
| filters = {
| akka-router {
- | includes = [ "user/tracked-*", "user/measuring-*", "user/stop-*" ]
- | excludes = [ "user/tracked-explicitly-excluded-*"]
+ | includes = [ "*/user/tracked-*", "*/user/measuring-*", "*/user/stop-*" ]
+ | excludes = [ "*/user/tracked-explicitly-excluded-*"]
| }
| }
|}
@@ -180,7 +180,7 @@ class RouterMetricsSpec extends BaseKamonSpec("router-metrics-spec") {
trackedRouter ! PoisonPill
deathWatcher.expectTerminated(trackedRouter)
- routerMetricsRecorderOf("user/stop-in-pool-router").get shouldNot be theSameInstanceAs (firstRecorder)
+ routerMetricsRecorderOf("user/stop-in-pool-router") shouldBe empty
}
"clean up the associated recorder when the group router is stopped" in new RouterMetricsFixtures {
@@ -193,7 +193,7 @@ class RouterMetricsSpec extends BaseKamonSpec("router-metrics-spec") {
trackedRouter ! PoisonPill
deathWatcher.expectTerminated(trackedRouter)
- routerMetricsRecorderOf("user/stop-in-group-router").get shouldNot be theSameInstanceAs (firstRecorder)
+ routerMetricsRecorderOf("user/stop-in-group-router") shouldBe empty
}
}
@@ -205,7 +205,7 @@ class RouterMetricsSpec extends BaseKamonSpec("router-metrics-spec") {
}
def routerMetricsRecorderOf(routerName: String): Option[RouterMetrics] =
- Kamon.metrics.register(RouterMetrics, routerName).map(_.recorder)
+ Kamon.metrics.find(system.name + "/" + routerName, RouterMetrics.category).map(_.asInstanceOf[RouterMetrics])
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/ActorLoggingInstrumentationSpec.scala b/kamon-akka/src/test/scala/kamon/akka/instrumentation/ActorLoggingInstrumentationSpec.scala
index 42a26cdd..85f41795 100644
--- a/kamon-akka/src/test/scala/kamon/akka/instrumentation/ActorLoggingInstrumentationSpec.scala
+++ b/kamon-akka/src/test/scala/kamon/akka/instrumentation/ActorLoggingInstrumentationSpec.scala
@@ -21,7 +21,7 @@ import com.typesafe.config.ConfigFactory
import kamon.testkit.BaseKamonSpec
import kamon.trace.TraceLocal.AvailableToMdc
import kamon.trace.logging.MdcKeysSupport
-import kamon.trace.{Tracer, TraceContextAware, TraceLocal}
+import kamon.trace.{ Tracer, TraceContextAware, TraceLocal }
import org.scalatest.Inspectors
import org.slf4j.MDC
diff --git a/kamon-akka/src/test/scala/kamon/akka/instrumentation/ActorSystemMessageInstrumentationSpec.scala b/kamon-akka/src/test/scala/kamon/akka/instrumentation/ActorSystemMessageInstrumentationSpec.scala
index fd9f58d0..1635fadc 100644
--- a/kamon-akka/src/test/scala/kamon/akka/instrumentation/ActorSystemMessageInstrumentationSpec.scala
+++ b/kamon-akka/src/test/scala/kamon/akka/instrumentation/ActorSystemMessageInstrumentationSpec.scala
@@ -21,7 +21,7 @@ import akka.actor._
import akka.testkit.ImplicitSender
import com.typesafe.config.ConfigFactory
import kamon.testkit.BaseKamonSpec
-import kamon.trace.{Tracer, EmptyTraceContext}
+import kamon.trace.{ Tracer, EmptyTraceContext }
import org.scalatest.WordSpecLike
import scala.concurrent.duration._
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 a44945ea..4268e53d 100644
--- a/kamon-akka/src/test/scala/kamon/akka/instrumentation/AskPatternInstrumentationSpec.scala
+++ b/kamon-akka/src/test/scala/kamon/akka/instrumentation/AskPatternInstrumentationSpec.scala
@@ -27,7 +27,7 @@ import com.typesafe.config.ConfigFactory
import kamon.Kamon
import kamon.akka.Akka
import kamon.testkit.BaseKamonSpec
-import kamon.trace.{Tracer, TraceContext, TraceContextAware}
+import kamon.trace.{ Tracer, TraceContext, TraceContextAware }
import scala.concurrent.duration._
diff --git a/kamon-annotation/src/main/java/kamon/annotation/Count.java b/kamon-annotation/src/main/java/kamon/annotation/Count.java
new file mode 100644
index 00000000..09bea4ec
--- /dev/null
+++ b/kamon-annotation/src/main/java/kamon/annotation/Count.java
@@ -0,0 +1,69 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * A marker annotation to define a method as a Counter.
+ * <p>
+ * <p>
+ * Given a method like this:
+ * <pre><code>
+ * {@literal @}Count(name = "coolName", tags="${'my-cool-tag':'my-cool-value'}")
+ * public String coolName(String name) {
+ * return "Hello " + name;
+ * }
+ * </code></pre>
+ * <p>
+ * <p>
+ * A {@link kamon.metric.instrument.Counter Counter} for the defining method with the name {@code coolName} will be created and each time the
+ * {@code #coolName(String)} method is invoked, the counter will be incremented.
+ */
+@Documented
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Count {
+
+ /**
+ * @return The counter's name.
+ * <p>
+ * Also, the Metric name can be resolved with an EL expression that evaluates to a String:
+ * <p>
+ * <pre>
+ * {@code
+ * class Counted {
+ * private long id;
+ *
+ * public long getId() { return id; }
+ *
+ * {@literal @}Count (name = "${'counterID:' += this.id}")
+ * void countedMethod() {} // create a counter with name => counterID:[id]
+ * }
+ * }
+ * </pre>
+ */
+ String name();
+
+ /**
+ * Tags are a way of adding dimensions to metrics,
+ * these are constructed using EL syntax e.g. "${'algorithm':'1','env':'production'}"
+ *
+ * @return the tags associated to the counter
+ */
+ String tags() default "";
+}
diff --git a/kamon-annotation/src/main/java/kamon/annotation/EnableKamon.java b/kamon-annotation/src/main/java/kamon/annotation/EnableKamon.java
new file mode 100644
index 00000000..97ed9375
--- /dev/null
+++ b/kamon-annotation/src/main/java/kamon/annotation/EnableKamon.java
@@ -0,0 +1,27 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * A marker annotation for enable the Kamon instrumentation.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface EnableKamon {}
diff --git a/kamon-annotation/src/main/java/kamon/annotation/Histogram.java b/kamon-annotation/src/main/java/kamon/annotation/Histogram.java
new file mode 100644
index 00000000..7c163104
--- /dev/null
+++ b/kamon-annotation/src/main/java/kamon/annotation/Histogram.java
@@ -0,0 +1,89 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * A marker annotation to define a method as a Histogram.
+ * <p>
+ * <p>
+ * Given a method like this:
+ * <pre><code>
+ * {@literal @}0Histogram(name = "coolName", tags="${'my-cool-tag':'my-cool-value'}")
+ * public (Long|Double|Float|Integer) coolName() {
+ * return someComputation();
+ * }
+ * </code></pre>
+ * <p>
+ * <p>
+ * A {@link kamon.metric.instrument.Histogram Histogram} for the defining method with the name {@code coolName} will be created which uses the
+ * annotated method's return as its value.
+ */
+@Documented
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Histogram {
+
+ /**
+ * @return The histogram's name.
+ * <p>
+ * Also, the Metric name can be resolved with an EL expression that evaluates to a String:
+ * <p>
+ * <pre>
+ * {@code
+ * class ClassWithHistogram {
+ * private long id;
+ *
+ * public long getId() { return id; }
+ *
+ * {@literal @}Histogram (name = "${'histoID:' += this.id}")
+ * void countedMethod() {} // create a histogram with name => histoID:[id]
+ * }
+ * }
+ * </pre>
+ */
+ String name();
+
+ /**
+ * The lowest value that can be discerned (distinguished from 0) by the histogram.Must be a positive integer that
+ * is >= 1. May be internally rounded down to nearest power of 2.
+ */
+ long lowestDiscernibleValue() default 1;
+
+
+ /**
+ * The highest value to be tracked by the histogram. Must be a positive integer that is >= (2 * lowestDiscernibleValue).
+ * Must not be larger than (Long.MAX_VALUE/2).
+ */
+ long highestTrackableValue() default 3600000000000L;
+
+ /**
+ * The number of significant decimal digits to which the histogram will maintain value resolution and separation.
+ * Must be a non-negative integer between 1 and 3.
+ */
+ int precision() default 2;
+
+
+ /**
+ * Tags are a way of adding dimensions to metrics,
+ * these are constructed using EL syntax e.g. "${'algorithm':'1','env':'production'}"
+ *
+ * @return the tags associated to the histogram
+ */
+ String tags() default "";
+} \ No newline at end of file
diff --git a/kamon-annotation/src/main/java/kamon/annotation/MinMaxCount.java b/kamon-annotation/src/main/java/kamon/annotation/MinMaxCount.java
new file mode 100644
index 00000000..ed2a6f6e
--- /dev/null
+++ b/kamon-annotation/src/main/java/kamon/annotation/MinMaxCount.java
@@ -0,0 +1,71 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * A marker annotation to define a method as a MinMaxCounter.
+ * <p>
+ * <p>
+ * Given a method like this:
+ * <pre><code>
+ * {@literal @}MinMaxCount(name = "coolName", tags="${'my-cool-tag':'my-cool-value'}")
+ * public String coolName(String name) {
+ * return "Hello " + name;
+ * }
+ * </code></pre>
+ * <p>
+ * <p>
+ * A {@link kamon.metric.instrument.MinMaxCounter MinMaxCounter} for the defining method with the name {@code coolName} will be created and each time the
+ * {@code #coolName(String)} method is invoked the counter is decremented when the method returns,
+ * counting current invocations of the annotated method.
+ */
+@Documented
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface MinMaxCount {
+
+ /**
+ * @return The MinMaxCounter's name.
+ * <p>
+ * Also, the Metric name can be resolved with an EL expression that evaluates to a String:
+ * <p>
+ * <pre>
+ * {@code
+ * class MinMaxCounted {
+ * private long id;
+ *
+ * public long getId() { return id; }
+ *
+ * {@literal @}MinMaxCount (name = "${'counterID:' += this.id}")
+ * void countedMethod() {} // create a counter with name => counterID:[id]
+ * }
+ * }
+ * </pre>
+ */
+
+ String name();
+
+ /**
+ * Tags are a way of adding dimensions to metrics,
+ * these are constructed using EL syntax e.g. "${'algorithm':'1','env':'production'}"
+ *
+ * @return the tags associated to the counter
+ */
+ String tags() default "";
+}
diff --git a/kamon-annotation/src/main/java/kamon/annotation/Segment.java b/kamon-annotation/src/main/java/kamon/annotation/Segment.java
new file mode 100644
index 00000000..8d69e84a
--- /dev/null
+++ b/kamon-annotation/src/main/java/kamon/annotation/Segment.java
@@ -0,0 +1,78 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * A marker annotation to start a new Segment.
+ * <p>
+ * <p>
+ * Given a method like this:
+ * <pre><code>
+ * {@literal @}Segment("coolSegmentName", tags="${'my-cool-tag':'my-cool-value'}")
+ * public String coolName(String name) {
+ * return "Hello " + name;
+ * }
+ * </code></pre>
+ * <p>
+ * <p>
+ * A new Segment will be created only if in the moment of the method execution exist a TraceContext.
+ */
+@Documented
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Segment {
+
+ /**
+ * @return The Segment's name.
+ * <p>
+ * Also, the Segment name can be resolved with an EL expression that evaluates to a String:
+ * <p>
+ * <pre>
+ * {@code
+ * class Segment {
+ * private long id;
+ *
+ * public long getId() { return id; }
+ *
+ * {@literal @}Segment (name = "${'segmentID:' += this.id}")
+ * void segment() {} // create a Segment with name => segmentID:[id]
+ * }
+ * }
+ * </pre>
+ */
+ String name();
+
+ /**
+ * @return The Segment's category.
+ */
+ String category();
+
+ /**
+ * @return The Segment's library.
+ */
+ String library();
+
+ /**
+ * Tags are a way of adding dimensions to metrics,
+ * these are constructed using EL syntax e.g. "${'algorithm':'1','env':'production'}"
+ *
+ * @return the tags associated to the segment
+ */
+ String tags() default "";
+}
diff --git a/kamon-annotation/src/main/java/kamon/annotation/Time.java b/kamon-annotation/src/main/java/kamon/annotation/Time.java
new file mode 100644
index 00000000..a8e3a62c
--- /dev/null
+++ b/kamon-annotation/src/main/java/kamon/annotation/Time.java
@@ -0,0 +1,57 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.annotation;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * A marker annotation to define a method as a timed.
+ * <p>
+ * <p>
+ * Given a method like this:
+ * <pre><code>
+ * {@literal @}Timed(name = "coolName", tags="""${{'my-cool-tag':'my-cool-value'}}""")
+ * public String coolName(String name) {
+ * return "Hello " + name;
+ * }
+ * </code></pre>
+ * <p>
+ * <p>
+ * A histogram for the defining method with the name {@code coolName} will be created and each time the
+ * {@code #coolName(String)} method is invoked, the latency of execution will be recorded.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Time {
+ /**
+ * @return The histogram's name.
+ */
+ String name();
+
+ /**
+ * Tags are a way of adding dimensions to metrics,
+ * these are constructed using EL syntax e.g. """${{'algorithm':'1','env':'production'}}"""
+ *
+ * @return the tags associated to the histogram
+ */
+ String tags() default "";
+}
diff --git a/kamon-annotation/src/main/java/kamon/annotation/Trace.java b/kamon-annotation/src/main/java/kamon/annotation/Trace.java
new file mode 100644
index 00000000..1f373fa2
--- /dev/null
+++ b/kamon-annotation/src/main/java/kamon/annotation/Trace.java
@@ -0,0 +1,69 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * A marker annotation to start a new Trace.
+ * <p>
+ * <p>
+ * Given a method like this:
+ * <pre><code>
+ * {@literal @}Trace("coolTraceName", tags="${'my-cool-tag':'my-cool-value'}")
+ * public String coolName(String name) {
+ * return "Hello " + name;
+ * }
+ * </code></pre>
+ * <p>
+ * <p>
+ * A new Trace will be created for the defining method with the name each time the
+ * {@code #coolName(String)} method is invoked.
+ */
+
+@Documented
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Trace {
+ /**
+ * @return The Trace's name.
+ * <p>
+ * Also, the Trace name can be resolved with an EL expression that evaluates to a String:
+ * <p>
+ * <pre>
+ * {@code
+ * class Traced {
+ * private long id;
+ *
+ * public long getId() { return id; }
+ *
+ * {@literal @}Trace (name = "${'traceID:' += this.id}")
+ * void countedMethod() {} // create a Trace with name => traceID:[id]
+ * }
+ * }
+ * </pre>
+ */
+ String value();
+
+ /**
+ * Tags are a way of adding dimensions to metrics,
+ * these are constructed using EL syntax e.g. "${'algorithm':'1','env':'production'}"
+ *
+ * @return the tags associated to the trace
+ */
+ String tags() default "";
+}
diff --git a/kamon-annotation/src/main/java/kamon/annotation/el/resolver/PrivateFieldELResolver.java b/kamon-annotation/src/main/java/kamon/annotation/el/resolver/PrivateFieldELResolver.java
new file mode 100644
index 00000000..df646942
--- /dev/null
+++ b/kamon-annotation/src/main/java/kamon/annotation/el/resolver/PrivateFieldELResolver.java
@@ -0,0 +1,117 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.annotation.el.resolver;
+
+import java.beans.FeatureDescriptor;
+import java.lang.reflect.Field;
+import java.util.Iterator;
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.ELResolver;
+
+/**
+ * A custom {@link ELResolver} for mapping properties to public/private fields of the base object.
+ */
+public class PrivateFieldELResolver extends ELResolver {
+
+ @Override
+ public Object getValue(ELContext context, Object base, Object property) {
+ if (base == null) {
+ return null;
+ }
+ try {
+ Field field = getField(base, (String) property);
+ context.setPropertyResolved(true);
+ field.setAccessible(true);
+ return field.get(base);
+ } catch (NoSuchFieldException exc) {
+ context.setPropertyResolved(false);
+ return null;
+ } catch (Exception exc) {
+ throw new ELException(exc);
+ }
+ }
+
+ private Field getField(Object base, String property) throws NoSuchFieldException {
+ try {
+ return base.getClass().getDeclaredField(property);
+ } catch (SecurityException exc) {
+ throw new ELException(exc);
+ }
+ }
+
+ @Override
+ public Class<?> getType(ELContext context, Object base, Object property) {
+ if (base == null) {
+ return null;
+ }
+ try {
+ Field field = getField(base, (String) property);
+ if (field != null) {
+ context.setPropertyResolved(true);
+ return field.getType();
+ } else {
+ return null;
+ }
+ } catch (NoSuchFieldException exc) {
+ context.setPropertyResolved(false);
+ return null;
+ }
+ }
+
+ @Override
+ public void setValue(ELContext context, Object base, Object property, Object value) {
+ if (base == null) {
+ return;
+ }
+ try {
+ context.setPropertyResolved(true);
+ getField(base, (String) property).set(base, value);
+ } catch (NoSuchFieldException exc) {
+ context.setPropertyResolved(false);
+ } catch (Exception exc) {
+ throw new ELException(exc);
+ }
+ }
+
+ @Override
+ public boolean isReadOnly(ELContext context, Object base, Object property) {
+ if (base == null) {
+ return true;
+ }
+ try {
+ Field field = getField(base, (String) property);
+ if (field != null) {
+ context.setPropertyResolved(true);
+ return !field.isAccessible();
+ }
+ } catch (NoSuchFieldException exc) {
+ context.setPropertyResolved(false);
+ }
+ return false;
+ }
+
+ @Override
+ public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
+ return null;
+ }
+
+ @Override
+ public Class<?> getCommonPropertyType(ELContext context, Object base) {
+ return String.class;
+ }
+} \ No newline at end of file
diff --git a/kamon-annotation/src/main/resources/META-INF/aop.xml b/kamon-annotation/src/main/resources/META-INF/aop.xml
new file mode 100644
index 00000000..b017e60e
--- /dev/null
+++ b/kamon-annotation/src/main/resources/META-INF/aop.xml
@@ -0,0 +1,18 @@
+<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
+
+<aspectj>
+ <aspects>
+ <aspect name="kamon.annotation.instrumentation.ClassToAnnotationInstrumentsMixin"/>
+ <aspect name="kamon.annotation.instrumentation.AnnotationInstrumentation"/>
+ <aspect name="kamon.annotation.instrumentation.StaticAnnotationInstrumentation"/>
+ </aspects>
+ <weaver options="-warn:none">
+ <!-- exclude some commons packages -->
+ <exclude within="org.apache.commons..*"/>
+ <exclude within="org.apache.log4j..*"/>
+ <exclude within="org.hibernate..*"/>
+ <exclude within="org.springframework..*"/>
+ <exclude within="com.google..*"/>
+ <exclude within="*..*CGLIB*" />
+ </weaver>
+</aspectj>
diff --git a/kamon-annotation/src/main/resources/reference.conf b/kamon-annotation/src/main/resources/reference.conf
new file mode 100644
index 00000000..b7680022
--- /dev/null
+++ b/kamon-annotation/src/main/resources/reference.conf
@@ -0,0 +1,19 @@
+# ========================================= #
+# Kamon-Annotation Reference Configuration #
+# ========================================= #
+
+kamon {
+ annotation {
+ # We use two arrays to store the kamon instruments in order to do fast array lookups.
+ # These lookups are done using the getId() method on the JoinPoint.StaticPart object.
+ # The ids for all affected join points within a target type are unique (and start from 0).
+ instruments-array-size = 32
+ }
+ modules {
+ kamon-annotation {
+ auto-start = yes
+ requires-aspectj = yes
+ extension-id = "kamon.annotation.Annotation"
+ }
+ }
+}
diff --git a/kamon-annotation/src/main/scala/kamon/annotation/Annotation.scala b/kamon-annotation/src/main/scala/kamon/annotation/Annotation.scala
new file mode 100644
index 00000000..6ddf57cf
--- /dev/null
+++ b/kamon-annotation/src/main/scala/kamon/annotation/Annotation.scala
@@ -0,0 +1,34 @@
+/* =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.annotation
+
+import akka.actor.{ ExtendedActorSystem, Extension, ExtensionId, ExtensionIdProvider }
+import akka.event.Logging
+import kamon.Kamon
+
+object Annotation extends ExtensionId[AnnotationExtension] with ExtensionIdProvider {
+ override def lookup(): ExtensionId[_ <: Extension] = Annotation
+ override def createExtension(system: ExtendedActorSystem): AnnotationExtension = new AnnotationExtension(system)
+}
+
+class AnnotationExtension(system: ExtendedActorSystem) extends Kamon.Extension {
+ val log = Logging(system, classOf[AnnotationExtension])
+ log.info(s"Starting the Kamon(Annotation) extension")
+
+ val config = system.settings.config.getConfig("kamon.annotation")
+ val arraySize = config.getInt("instruments-array-size")
+}
+
diff --git a/kamon-core/src/main/scala/kamon/instrumentation/AspectJWeaverMissingWarning.scala b/kamon-annotation/src/main/scala/kamon/annotation/el/ELProcessorFactory.scala
index 4dc5ff41..e80c61d8 100644
--- a/kamon-core/src/main/scala/kamon/instrumentation/AspectJWeaverMissingWarning.scala
+++ b/kamon-annotation/src/main/scala/kamon/annotation/el/ELProcessorFactory.scala
@@ -14,20 +14,27 @@
* =========================================================================================
*/
-package kamon.instrumentation
+package kamon.annotation.el
-import _root_.akka.event.EventStream
-import org.aspectj.lang.ProceedingJoinPoint
-import org.aspectj.lang.annotation.{ Around, Pointcut, Aspect }
+import javax.el.ELProcessor
+import kamon.annotation.el.resolver.PrivateFieldELResolver
-@Aspect
-class AspectJWeaverMissingWarning {
+object ELProcessorFactory {
+ def withClass(clazz: Class[_]): ELProcessor = {
+ val processor = create()
+ processor.getELManager.importClass(clazz.getName)
+ processor
+ }
- @Pointcut("execution(* kamon.metric.MetricsExtension.printInitializationMessage(..)) && args(eventStream, *)")
- def printInitializationMessage(eventStream: EventStream): Unit = {}
+ def withObject(obj: AnyRef): ELProcessor = {
+ val processor = withClass(obj.getClass)
+ processor.defineBean("this", obj)
+ processor
+ }
- @Around("printInitializationMessage(eventStream)")
- def aroundPrintInitializationMessage(pjp: ProceedingJoinPoint, eventStream: EventStream): Unit = {
- pjp.proceed(Array[AnyRef](eventStream, Boolean.box(true)))
+ private def create(): ELProcessor = {
+ val processor = new ELProcessor()
+ processor.getELManager.addELResolver(new PrivateFieldELResolver())
+ processor
}
}
diff --git a/kamon-annotation/src/main/scala/kamon/annotation/el/EnhancedELProcessor.scala b/kamon-annotation/src/main/scala/kamon/annotation/el/EnhancedELProcessor.scala
new file mode 100644
index 00000000..f407930b
--- /dev/null
+++ b/kamon-annotation/src/main/scala/kamon/annotation/el/EnhancedELProcessor.scala
@@ -0,0 +1,60 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.annotation.el
+
+import javax.el.ELProcessor
+
+import kamon.Kamon
+import kamon.annotation.Annotation
+
+import scala.util.{ Failure, Success, Try }
+
+/**
+ * Pimp ELProcessor injecting some useful methods.
+ */
+object EnhancedELProcessor {
+ private val Pattern = """[#|$]\{(.*)\}""".r
+
+ implicit class Syntax(val processor: ELProcessor) extends AnyVal {
+ import scala.collection.JavaConverters._
+
+ def evalToString(expression: String): String = extract(expression).map { str ⇒
+ eval[String](str) match {
+ case Success(value) ⇒ value
+ case Failure(cause) ⇒
+ Kamon(Annotation).log.error(s"${cause.getMessage} -> we will complete the operation with 'unknown' string")
+ "unknown"
+ }
+ } getOrElse expression
+
+ def evalToMap(expression: String): Map[String, String] = extract(expression).map { str ⇒
+ eval[Map[String, String]](s"{$str}") match {
+ case Success(value) ⇒ value.asInstanceOf[java.util.HashMap[String, String]].asScala.toMap
+ case Failure(cause) ⇒
+ Kamon(Annotation).log.error(s"${cause.getMessage} -> we will complete the operation with an empty map")
+ Map.empty[String, String]
+ }
+ } getOrElse Map.empty[String, String]
+
+ private def eval[A](str: String): Try[A] = Try(processor.eval(str).asInstanceOf[A])
+
+ private def extract(expression: String): Option[String] = expression match {
+ case Pattern(ex) ⇒ Some(ex)
+ case _ ⇒ None
+ }
+ }
+}
diff --git a/kamon-annotation/src/main/scala/kamon/annotation/instrumentation/AnnotationInstrumentation.scala b/kamon-annotation/src/main/scala/kamon/annotation/instrumentation/AnnotationInstrumentation.scala
new file mode 100644
index 00000000..381aeb72
--- /dev/null
+++ b/kamon-annotation/src/main/scala/kamon/annotation/instrumentation/AnnotationInstrumentation.scala
@@ -0,0 +1,81 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.annotation.instrumentation
+
+import kamon.Kamon
+import kamon.annotation._
+import kamon.metric.instrument
+import kamon.metric.instrument.{ MinMaxCounter, Counter }
+import org.aspectj.lang.annotation._
+import org.aspectj.lang.{ JoinPoint, ProceedingJoinPoint }
+import java.util.concurrent.atomic.AtomicReferenceArray
+
+@Aspect
+class AnnotationInstrumentation extends BaseAnnotationInstrumentation {
+
+ @After("execution((@kamon.annotation.EnableKamon AnnotationInstruments+).new(..)) && this(obj)")
+ def creation(jps: JoinPoint.StaticPart, obj: AnnotationInstruments): Unit = {
+ val size = Kamon(Annotation).arraySize
+ obj.traces = new AtomicReferenceArray[TraceContextInfo](size)
+ obj.segments = new AtomicReferenceArray[SegmentInfo](size)
+ obj.counters = new AtomicReferenceArray[Counter](size)
+ obj.minMaxCounters = new AtomicReferenceArray[MinMaxCounter](size)
+ obj.histograms = new AtomicReferenceArray[instrument.Histogram](size)
+ }
+
+ @Around("execution(@kamon.annotation.Trace !static * (@kamon.annotation.EnableKamon AnnotationInstruments+).*(..)) && this(obj)")
+ def trace(pjp: ProceedingJoinPoint, obj: AnnotationInstruments): AnyRef = {
+ var traceInfo = obj.traces.get(pjp.getStaticPart.getId)
+ if (traceInfo == null) traceInfo = registerTrace(pjp.getStaticPart, obj.traces, StringEvaluator(obj), TagsEvaluator(obj))
+ processTrace(traceInfo, pjp)
+ }
+
+ @Around("execution(@kamon.annotation.Segment !static * (@kamon.annotation.EnableKamon AnnotationInstruments+).*(..)) && this(obj)")
+ def segment(pjp: ProceedingJoinPoint, obj: AnnotationInstruments): AnyRef = {
+ var segmentInfo = obj.segments.get(pjp.getStaticPart.getId)
+ if (segmentInfo == null) segmentInfo = registerSegment(pjp.getStaticPart, obj.segments, StringEvaluator(obj), TagsEvaluator(obj))
+ processSegment(segmentInfo, pjp)
+ }
+
+ @Around("execution(@kamon.annotation.Time !static * (@kamon.annotation.EnableKamon AnnotationInstruments+).*(..)) && this(obj)")
+ def time(pjp: ProceedingJoinPoint, obj: AnnotationInstruments): AnyRef = {
+ var histogram = obj.histograms.get(pjp.getStaticPart.getId)
+ if (histogram == null) histogram = registerTime(pjp.getStaticPart, obj.histograms, StringEvaluator(obj), TagsEvaluator(obj))
+ processTime(histogram, pjp)
+ }
+
+ @Around("execution(@kamon.annotation.Count !static * (@kamon.annotation.EnableKamon AnnotationInstruments+).*(..)) && this(obj)")
+ def count(pjp: ProceedingJoinPoint, obj: AnnotationInstruments): AnyRef = {
+ var counter = obj.counters.get(pjp.getStaticPart.getId)
+ if (counter == null) counter = registerCounter(pjp.getStaticPart, obj.counters, StringEvaluator(obj), TagsEvaluator(obj))
+ processCount(counter, pjp)
+ }
+
+ @Around("execution(@kamon.annotation.MinMaxCount !static * (@kamon.annotation.EnableKamon AnnotationInstruments+).*(..)) && this(obj)")
+ def minMax(pjp: ProceedingJoinPoint, obj: AnnotationInstruments): AnyRef = {
+ var minMax = obj.minMaxCounters.get(pjp.getStaticPart.getId)
+ if (minMax == null) minMax = registerMinMaxCounter(pjp.getStaticPart, obj.minMaxCounters, StringEvaluator(obj), TagsEvaluator(obj))
+ processMinMax(minMax, pjp)
+ }
+
+ @AfterReturning(pointcut = "execution(@kamon.annotation.Histogram !static (int || long || double || float) (@kamon.annotation.EnableKamon AnnotationInstruments+).*(..)) && this(obj)", returning = "result")
+ def histogram(jps: JoinPoint.StaticPart, obj: AnnotationInstruments, result: AnyRef): Unit = {
+ var histogram = obj.histograms.get(jps.getId)
+ if (histogram == null) histogram = registerHistogram(jps, obj.histograms, StringEvaluator(obj), TagsEvaluator(obj))
+ processHistogram(histogram, result, jps)
+ }
+}
diff --git a/kamon-annotation/src/main/scala/kamon/annotation/instrumentation/BaseAnnotationInstrumentation.scala b/kamon-annotation/src/main/scala/kamon/annotation/instrumentation/BaseAnnotationInstrumentation.scala
new file mode 100644
index 00000000..2074e237
--- /dev/null
+++ b/kamon-annotation/src/main/scala/kamon/annotation/instrumentation/BaseAnnotationInstrumentation.scala
@@ -0,0 +1,178 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.annotation.instrumentation
+
+import java.util.concurrent.atomic.AtomicReferenceArray
+import javax.el.ELProcessor
+
+import kamon.Kamon
+import kamon.annotation.el.{ EnhancedELProcessor, ELProcessorFactory }
+import kamon.annotation.{ Histogram, _ }
+import kamon.metric.instrument.Histogram.DynamicRange
+import kamon.metric.instrument.{ Counter, MinMaxCounter }
+import kamon.metric._
+import kamon.trace.Tracer
+import kamon.util.Latency
+import org.aspectj.lang.{ JoinPoint, ProceedingJoinPoint }
+import org.aspectj.lang.annotation.{ Aspect, DeclareMixin }
+import org.aspectj.lang.reflect.MethodSignature
+import EnhancedELProcessor.Syntax
+
+class BaseAnnotationInstrumentation {
+
+ @inline final def registerTime(jps: JoinPoint.StaticPart, histograms: AtomicReferenceArray[instrument.Histogram], evalString: StringEvaluator, evalTags: TagsEvaluator): instrument.Histogram = {
+ val method = jps.getSignature.asInstanceOf[MethodSignature].getMethod
+ val time = method.getAnnotation(classOf[Time])
+ val name = evalString(time.name())
+ val tags = evalTags(time.tags())
+ val currentHistogram = Kamon.metrics.histogram(name, tags)
+ histograms.set(jps.getId, currentHistogram)
+ currentHistogram
+ }
+
+ @inline final def registerHistogram(jps: JoinPoint.StaticPart, histograms: AtomicReferenceArray[instrument.Histogram], evalString: StringEvaluator, evalTags: TagsEvaluator): instrument.Histogram = {
+ val method = jps.getSignature.asInstanceOf[MethodSignature].getMethod
+ val histogram = method.getAnnotation(classOf[Histogram])
+ val name = evalString(histogram.name())
+ val tags = evalTags(histogram.tags())
+ val dynamicRange = DynamicRange(histogram.lowestDiscernibleValue(), histogram.highestTrackableValue(), histogram.precision())
+ val currentHistogram = Kamon.metrics.histogram(name, tags, dynamicRange)
+ histograms.set(jps.getId, currentHistogram)
+ currentHistogram
+ }
+
+ @inline final def registerMinMaxCounter(jps: JoinPoint.StaticPart, minMaxCounters: AtomicReferenceArray[MinMaxCounter], evalString: StringEvaluator, evalTags: TagsEvaluator): instrument.MinMaxCounter = {
+ val method = jps.getSignature.asInstanceOf[MethodSignature].getMethod
+ val minMaxCount = method.getAnnotation(classOf[MinMaxCount])
+ val name = evalString(minMaxCount.name())
+ val tags = evalTags(minMaxCount.tags())
+ val minMaxCounter = Kamon.metrics.minMaxCounter(name, tags)
+ minMaxCounters.set(jps.getId, minMaxCounter)
+ minMaxCounter
+ }
+
+ @inline final def registerCounter(jps: JoinPoint.StaticPart, counters: AtomicReferenceArray[Counter], evalString: StringEvaluator, evalTags: TagsEvaluator): instrument.Counter = {
+ val method = jps.getSignature.asInstanceOf[MethodSignature].getMethod
+ val count = method.getAnnotation(classOf[Count])
+ val name = evalString(count.name())
+ val tags = evalTags(count.tags())
+ val counter = Kamon.metrics.counter(name, tags)
+ counters.set(jps.getId, counter)
+ counter
+ }
+
+ @inline final def registerTrace(jps: JoinPoint.StaticPart, traces: AtomicReferenceArray[TraceContextInfo], evalString: StringEvaluator, evalTags: TagsEvaluator): TraceContextInfo = {
+ val method = jps.getSignature.asInstanceOf[MethodSignature].getMethod
+ val trace = method.getAnnotation(classOf[Trace])
+ val name = evalString(trace.value())
+ val tags = evalTags(trace.tags())
+ val traceContextInfo = TraceContextInfo(name, tags)
+ traces.set(jps.getId, traceContextInfo)
+ traceContextInfo
+ }
+
+ @inline final def registerSegment(jps: JoinPoint.StaticPart, segments: AtomicReferenceArray[SegmentInfo], evalString: StringEvaluator, evalTags: TagsEvaluator): SegmentInfo = {
+ val method = jps.getSignature.asInstanceOf[MethodSignature].getMethod
+ val segment = method.getAnnotation(classOf[Segment])
+ val name = evalString(segment.name())
+ val category = evalString(segment.category())
+ val library = evalString(segment.library())
+ val tags = evalTags(segment.tags())
+ val segmentInfo = SegmentInfo(name, category, library, tags)
+ segments.set(jps.getId, segmentInfo)
+ segmentInfo
+ }
+
+ @inline final def processTrace(traceInfo: TraceContextInfo, pjp: ProceedingJoinPoint): AnyRef = {
+ Tracer.withContext(Kamon.tracer.newContext(traceInfo.name)) {
+ traceInfo.tags.foreach { case (key, value) ⇒ Tracer.currentContext.addMetadata(key, value) }
+ val result = pjp.proceed()
+ Tracer.currentContext.finish()
+ result
+ }
+ }
+
+ @inline final def processSegment(segmentInfo: SegmentInfo, pjp: ProceedingJoinPoint): AnyRef = {
+ Tracer.currentContext.collect { ctx ⇒
+ val currentSegment = ctx.startSegment(segmentInfo.name, segmentInfo.category, segmentInfo.library)
+ segmentInfo.tags.foreach { case (key, value) ⇒ currentSegment.addMetadata(key, value) }
+ val result = pjp.proceed()
+ currentSegment.finish()
+ result
+ } getOrElse pjp.proceed()
+ }
+
+ @inline final def processTime(histogram: instrument.Histogram, pjp: ProceedingJoinPoint): AnyRef = {
+ Latency.measure(histogram)(pjp.proceed)
+ }
+
+ @inline final def processHistogram(histogram: instrument.Histogram, result: AnyRef, jps: JoinPoint.StaticPart): Unit = {
+ histogram.record(result.asInstanceOf[Number].longValue())
+ }
+
+ final def processCount(counter: instrument.Counter, pjp: ProceedingJoinPoint): AnyRef = {
+ try pjp.proceed() finally counter.increment()
+ }
+
+ final def processMinMax(minMaxCounter: instrument.MinMaxCounter, pjp: ProceedingJoinPoint): AnyRef = {
+ minMaxCounter.increment()
+ try pjp.proceed() finally minMaxCounter.decrement()
+ }
+
+ private[annotation] def declaringType(signature: org.aspectj.lang.Signature) = signature.getDeclaringType
+}
+
+@Aspect
+class ClassToAnnotationInstrumentsMixin {
+ @DeclareMixin("(@kamon.annotation.EnableKamon *)")
+ def mixinClassToAnnotationInstruments: AnnotationInstruments = new AnnotationInstruments {}
+}
+
+trait AnnotationInstruments {
+ var traces: AtomicReferenceArray[TraceContextInfo] = _
+ var segments: AtomicReferenceArray[SegmentInfo] = _
+ var histograms: AtomicReferenceArray[instrument.Histogram] = _
+ var counters: AtomicReferenceArray[Counter] = _
+ var minMaxCounters: AtomicReferenceArray[MinMaxCounter] = _
+}
+
+case class SegmentInfo(name: String, category: String, library: String, tags: Map[String, String])
+case class TraceContextInfo(name: String, tags: Map[String, String])
+
+abstract class StringEvaluator(val processor: ELProcessor) extends (String ⇒ String)
+
+object StringEvaluator {
+ def apply(obj: AnyRef) = new StringEvaluator(ELProcessorFactory.withObject(obj)) {
+ def apply(str: String) = processor.evalToString(str)
+ }
+
+ def apply(clazz: Class[_]) = new StringEvaluator(ELProcessorFactory.withClass(clazz)) {
+ def apply(str: String) = processor.evalToString(str)
+ }
+}
+
+abstract class TagsEvaluator(val processor: ELProcessor) extends (String ⇒ Map[String, String])
+
+object TagsEvaluator {
+ def apply(obj: AnyRef) = new TagsEvaluator(ELProcessorFactory.withObject(obj)) {
+ def apply(str: String) = processor.evalToMap(str)
+ }
+
+ def apply(clazz: Class[_]) = new TagsEvaluator(ELProcessorFactory.withClass(clazz)) {
+ def apply(str: String) = processor.evalToMap(str)
+ }
+} \ No newline at end of file
diff --git a/kamon-annotation/src/main/scala/kamon/annotation/instrumentation/StaticAnnotationInstrumentation.scala b/kamon-annotation/src/main/scala/kamon/annotation/instrumentation/StaticAnnotationInstrumentation.scala
new file mode 100644
index 00000000..24ce75fd
--- /dev/null
+++ b/kamon-annotation/src/main/scala/kamon/annotation/instrumentation/StaticAnnotationInstrumentation.scala
@@ -0,0 +1,100 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.annotation.instrumentation
+
+import java.util.concurrent.atomic.AtomicReferenceArray
+
+import kamon.Kamon
+import kamon.annotation.Annotation
+import kamon.metric.instrument
+import kamon.metric.instrument.{ Counter, MinMaxCounter }
+import org.aspectj.lang.annotation.{ After, AfterReturning, Around, Aspect }
+import org.aspectj.lang.{ JoinPoint, ProceedingJoinPoint }
+
+@Aspect("pertypewithin(kamon.annotation.instrumentation.AnnotationInstruments+ && !kamon.annotation.instrumentation.*)")
+class StaticAnnotationInstrumentation extends BaseAnnotationInstrumentation with AnnotationInstruments {
+
+ @After("staticinitialization(*) && !within(kamon.annotation.instrumentation.*)")
+ def creation(jps: JoinPoint.StaticPart): Unit = {
+ val size = Kamon(Annotation).arraySize
+ traces = new AtomicReferenceArray[TraceContextInfo](size)
+ segments = new AtomicReferenceArray[SegmentInfo](size)
+ counters = new AtomicReferenceArray[Counter](size)
+ minMaxCounters = new AtomicReferenceArray[MinMaxCounter](size)
+ histograms = new AtomicReferenceArray[instrument.Histogram](size)
+ }
+
+ @Around("execution(@kamon.annotation.Trace static * (@kamon.annotation.EnableKamon *).*(..))")
+ def trace(pjp: ProceedingJoinPoint): AnyRef = {
+ var traceInfo = traces.get(pjp.getStaticPart.getId)
+ if (traceInfo == null) {
+ val clazz = declaringType(pjp.getSignature)
+ traceInfo = registerTrace(pjp.getStaticPart, traces, StringEvaluator(clazz), TagsEvaluator(clazz))
+ }
+ processTrace(traceInfo, pjp)
+ }
+
+ @Around("execution(@kamon.annotation.Segment static * (@kamon.annotation.EnableKamon *).*(..))")
+ def segment(pjp: ProceedingJoinPoint): AnyRef = {
+ var segmentInfo = segments.get(pjp.getStaticPart.getId)
+ if (segmentInfo == null) {
+ val clazz = declaringType(pjp.getSignature)
+ segmentInfo = registerSegment(pjp.getStaticPart, segments, StringEvaluator(clazz), TagsEvaluator(clazz))
+ }
+ processSegment(segmentInfo, pjp)
+ }
+
+ @Around("execution(@kamon.annotation.Time static * (@kamon.annotation.EnableKamon *).*(..))")
+ def time(pjp: ProceedingJoinPoint): AnyRef = {
+ var histogram = histograms.get(pjp.getStaticPart.getId)
+ if (histogram == null) {
+ val clazz = declaringType(pjp.getSignature)
+ histogram = registerTime(pjp.getStaticPart, histograms, StringEvaluator(clazz), TagsEvaluator(clazz))
+ }
+ processTime(histogram, pjp)
+ }
+
+ @Around("execution(@kamon.annotation.Count static * (@kamon.annotation.EnableKamon *).*(..))")
+ def count(pjp: ProceedingJoinPoint): AnyRef = {
+ var counter = counters.get(pjp.getStaticPart.getId)
+ if (counter == null) {
+ val clazz = declaringType(pjp.getSignature)
+ counter = registerCounter(pjp.getStaticPart, counters, StringEvaluator(clazz), TagsEvaluator(clazz))
+ }
+ processCount(counter, pjp)
+ }
+
+ @Around("execution(@kamon.annotation.MinMaxCount static * (@kamon.annotation.EnableKamon *).*(..))")
+ def minMax(pjp: ProceedingJoinPoint): AnyRef = {
+ var minMax = minMaxCounters.get(pjp.getStaticPart.getId)
+ if (minMax == null) {
+ val clazz = declaringType(pjp.getSignature)
+ minMax = registerMinMaxCounter(pjp.getStaticPart, minMaxCounters, StringEvaluator(clazz), TagsEvaluator(clazz))
+ }
+ processMinMax(minMax, pjp)
+ }
+
+ @AfterReturning(pointcut = "execution(@kamon.annotation.Histogram static (int || long || double || float) (@kamon.annotation.EnableKamon *).*(..))", returning = "result")
+ def histogram(jps: JoinPoint.StaticPart, result: AnyRef): Unit = {
+ var histogram = histograms.get(jps.getId)
+ if (histogram == null) {
+ val clazz = declaringType(jps.getSignature)
+ histogram = registerHistogram(jps, histograms, StringEvaluator(clazz), TagsEvaluator(clazz))
+ }
+ processHistogram(histogram, result, jps)
+ }
+}
diff --git a/kamon-annotation/src/test/java/kamon/annotation/AnnotatedJavaClass.java b/kamon-annotation/src/test/java/kamon/annotation/AnnotatedJavaClass.java
new file mode 100644
index 00000000..285ccc74
--- /dev/null
+++ b/kamon-annotation/src/test/java/kamon/annotation/AnnotatedJavaClass.java
@@ -0,0 +1,66 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.annotation;
+
+@EnableKamon
+public class AnnotatedJavaClass {
+
+ public static String ID = "10";
+
+ @Trace("trace")
+ public static void trace() {}
+
+ @Trace("trace-with-segment")
+ public static void segment() {
+ inner(); // method annotated with @Segment
+ }
+
+ @Trace("trace-with-segment-el")
+ public static void segmentWithEL() {
+ innerWithEL(); // method annotated with @Segment
+ }
+
+ @Count(name = "count")
+ public static void count() {}
+
+ @Count(name = "${'count:' += AnnotatedJavaClass.ID}", tags = "${'counter':'1', 'env':'prod'}")
+ public static void countWithEL() {}
+
+ @MinMaxCount(name = "minMax")
+ public static void countMinMax() {}
+
+ @MinMaxCount(name = "#{'minMax:' += AnnotatedJavaClass.ID}", tags = "#{'minMax':'1', 'env':'dev'}")
+ public static void countMinMaxWithEL() {}
+
+ @Time(name = "time")
+ public static void time() {}
+
+ @Time(name = "${'time:' += AnnotatedJavaClass.ID}", tags = "${'slow-service':'service', 'env':'prod'}")
+ public static void timeWithEL() {}
+
+ @Histogram(name = "histogram")
+ public static long histogram(Long value) { return value; }
+
+ @Histogram(name = "#{'histogram:' += AnnotatedJavaClass.ID}", tags = "${'histogram':'hdr', 'env':'prod'}")
+ public static long histogramWithEL(Long value) { return value;}
+
+ @Segment(name = "inner-segment", category = "inner", library = "segment")
+ private static void inner() {}
+
+ @Segment(name = "#{'inner-segment:' += AnnotatedJavaClass.ID}", category = "segments", library = "segment")
+ private static void innerWithEL() {}
+} \ No newline at end of file
diff --git a/kamon-annotation/src/test/resources/logback.xml b/kamon-annotation/src/test/resources/logback.xml
new file mode 100644
index 00000000..c336bbfe
--- /dev/null
+++ b/kamon-annotation/src/test/resources/logback.xml
@@ -0,0 +1,12 @@
+<configuration>
+ <statusListener class="ch.qos.logback.core.status.NopStatusListener"/>
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <root level="OFF">
+ <appender-ref ref="STDOUT"/>
+ </root>
+</configuration> \ No newline at end of file
diff --git a/kamon-annotation/src/test/scala/kamon/annotation/AnnotationInstrumentationSpec.scala b/kamon-annotation/src/test/scala/kamon/annotation/AnnotationInstrumentationSpec.scala
new file mode 100644
index 00000000..3d94d246
--- /dev/null
+++ b/kamon-annotation/src/test/scala/kamon/annotation/AnnotationInstrumentationSpec.scala
@@ -0,0 +1,199 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.annotation
+
+import com.typesafe.config.ConfigFactory
+import kamon.metric._
+import kamon.testkit.BaseKamonSpec
+import kamon.trace.SegmentCategory
+
+class AnnotationInstrumentationSpec extends BaseKamonSpec("annotation-instrumentation-spec") {
+ override lazy val config =
+ ConfigFactory.parseString(
+ """
+ |kamon.metric {
+ | tick-interval = 1 hour
+ | default-collection-context-buffer-size = 100
+ |}
+ """.stripMargin)
+
+ "the Kamon Annotation module" should {
+ "create a new trace when is invoked a method annotated with @Trace" in {
+ for (id ← 1 to 10) Annotated(id).trace()
+
+ val snapshot = takeSnapshotOf("trace", "trace")
+ snapshot.histogram("elapsed-time").get.numberOfMeasurements should be(10)
+ }
+
+ "create a segment when is invoked a method annotated with @Segment" in {
+ for (id ← 1 to 10) Annotated().segment()
+
+ val snapshot = takeSnapshotOf("trace-with-segment", "trace")
+ snapshot.histogram("elapsed-time").get.numberOfMeasurements should be(10)
+
+ val segmentMetricsSnapshot = takeSnapshotOf("inner-segment", "trace-segment",
+ tags = Map(
+ "trace" -> "trace-with-segment",
+ "category" -> "inner",
+ "library" -> "segment"))
+
+ segmentMetricsSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(10)
+ }
+
+ "create a segment when is invoked a method annotated with @Segment and evaluate EL expressions" in {
+ for (id ← 1 to 10) Annotated(id).segmentWithEL()
+
+ val snapshot = takeSnapshotOf("trace-with-segment-el", "trace")
+ snapshot.histogram("elapsed-time").get.numberOfMeasurements should be(10)
+
+ val segmentMetricsSnapshot = takeSnapshotOf("inner-segment:1", "trace-segment",
+ tags = Map(
+ "trace" -> "trace-with-segment-el",
+ "category" -> "inner",
+ "library" -> "segment"))
+
+ segmentMetricsSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(1)
+ }
+
+ "count the invocations of a method annotated with @Count" in {
+ for (id ← 1 to 10) Annotated(id).count()
+
+ val snapshot = takeSnapshotOf("count", "counter")
+ snapshot.counter("counter").get.count should be(10)
+ }
+
+ "count the invocations of a method annotated with @Count and evaluate EL expressions" in {
+ for (id ← 1 to 2) Annotated(id).countWithEL()
+
+ val counter1Snapshot = takeSnapshotOf("count:1", "counter", Map("counter" -> "1", "env" -> "prod"))
+ counter1Snapshot.counter("counter").get.count should be(1)
+
+ val counter2Snapshot = takeSnapshotOf("count:2", "counter", Map("counter" -> "1", "env" -> "prod"))
+ counter2Snapshot.counter("counter").get.count should be(1)
+ }
+
+ "count the current invocations of a method annotated with @MinMaxCount" in {
+ for (id ← 1 to 10) {
+ Annotated(id).countMinMax()
+ }
+
+ val snapshot = takeSnapshotOf("minMax", "min-max-counter")
+ snapshot.minMaxCounter("min-max-counter").get.max should be(1)
+ }
+
+ "count the current invocations of a method annotated with @MinMaxCount and evaluate EL expressions" in {
+ for (id ← 1 to 10) Annotated(id).countMinMaxWithEL()
+
+ val minMaxCounter1Snapshot = takeSnapshotOf("minMax:1", "min-max-counter", tags = Map("minMax" -> "1", "env" -> "dev"))
+ minMaxCounter1Snapshot.minMaxCounter("min-max-counter").get.sum should be(1)
+
+ val minMaxCounter2Snapshot = takeSnapshotOf("minMax:2", "min-max-counter", tags = Map("minMax" -> "1", "env" -> "dev"))
+ minMaxCounter2Snapshot.minMaxCounter("min-max-counter").get.sum should be(1)
+ }
+
+ "measure the time spent in the execution of a method annotated with @Time" in {
+ for (id ← 1 to 1) Annotated(id).time()
+
+ val snapshot = takeSnapshotOf("time", "histogram")
+ snapshot.histogram("histogram").get.numberOfMeasurements should be(1)
+ }
+
+ "measure the time spent in the execution of a method annotated with @Time and evaluate EL expressions" in {
+ for (id ← 1 to 1) Annotated(id).timeWithEL()
+
+ val snapshot = takeSnapshotOf("time:1", "histogram", tags = Map("slow-service" -> "service", "env" -> "prod"))
+ snapshot.histogram("histogram").get.numberOfMeasurements should be(1)
+ }
+
+ "record the value returned by a method annotated with @Histogram" in {
+ for (value ← 1 to 5) Annotated().histogram(value)
+
+ val snapshot = takeSnapshotOf("histogram", "histogram")
+ snapshot.histogram("histogram").get.numberOfMeasurements should be(5)
+ snapshot.histogram("histogram").get.min should be(1)
+ snapshot.histogram("histogram").get.max should be(5)
+ snapshot.histogram("histogram").get.sum should be(15)
+ }
+
+ "record the value returned by a method annotated with @Histogram and evaluate EL expressions" in {
+ for (value ← 1 to 2) Annotated(value).histogramWithEL(value)
+
+ val snapshot1 = takeSnapshotOf("histogram:1", "histogram", tags = Map("histogram" -> "hdr", "env" -> "prod"))
+ snapshot1.histogram("histogram").get.numberOfMeasurements should be(1)
+ snapshot1.histogram("histogram").get.min should be(1)
+ snapshot1.histogram("histogram").get.max should be(1)
+ snapshot1.histogram("histogram").get.sum should be(1)
+
+ val snapshot2 = takeSnapshotOf("histogram:2", "histogram", tags = Map("histogram" -> "hdr", "env" -> "prod"))
+ snapshot2.histogram("histogram").get.numberOfMeasurements should be(1)
+ snapshot2.histogram("histogram").get.min should be(2)
+ snapshot2.histogram("histogram").get.max should be(2)
+ snapshot2.histogram("histogram").get.sum should be(2)
+ }
+ }
+}
+
+@EnableKamon
+case class Annotated(id: Long) {
+
+ @Trace("trace")
+ def trace(): Unit = {}
+
+ @Trace("trace-with-segment")
+ def segment(): Unit = {
+ inner() // method annotated with @Segment
+ }
+
+ @Trace("trace-with-segment-el")
+ def segmentWithEL(): Unit = {
+ innerWithEL() // method annotated with @Segment
+ }
+
+ @Count(name = "count")
+ def count(): Unit = {}
+
+ @Count(name = "${'count:' += this.id}", tags = "${'counter':'1', 'env':'prod'}")
+ def countWithEL(): Unit = {}
+
+ @MinMaxCount(name = "minMax")
+ def countMinMax(): Unit = {}
+
+ @MinMaxCount(name = "#{'minMax:' += this.id}", tags = "#{'minMax':'1', 'env':'dev'}")
+ def countMinMaxWithEL(): Unit = {}
+
+ @Time(name = "time")
+ def time(): Unit = {}
+
+ @Time(name = "${'time:' += this.id}", tags = "${'slow-service':'service', 'env':'prod'}")
+ def timeWithEL(): Unit = {}
+
+ @Histogram(name = "histogram")
+ def histogram(value: Long): Long = value
+
+ @Histogram(name = "#{'histogram:' += this.id}", tags = "${'histogram':'hdr', 'env':'prod'}")
+ def histogramWithEL(value: Long): Long = value
+
+ @Segment(name = "inner-segment", category = "inner", library = "segment")
+ private def inner(): Unit = {}
+
+ @Segment(name = "#{'inner-segment:' += this.id}", category = "inner", library = "segment")
+ private def innerWithEL(): Unit = {}
+}
+
+object Annotated {
+ def apply(): Annotated = new Annotated(0L)
+} \ No newline at end of file
diff --git a/kamon-annotation/src/test/scala/kamon/annotation/StaticAnnotationInstrumentationJavaSpec.scala b/kamon-annotation/src/test/scala/kamon/annotation/StaticAnnotationInstrumentationJavaSpec.scala
new file mode 100644
index 00000000..6e0fbd02
--- /dev/null
+++ b/kamon-annotation/src/test/scala/kamon/annotation/StaticAnnotationInstrumentationJavaSpec.scala
@@ -0,0 +1,130 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.annotation
+
+import com.typesafe.config.ConfigFactory
+import kamon.metric.{ HistogramKey, MinMaxCounterKey, CounterKey }
+import kamon.testkit.BaseKamonSpec
+
+class StaticAnnotationInstrumentationJavaSpec extends BaseKamonSpec("static-annotation-instrumentation-java-spec") {
+ override lazy val config =
+ ConfigFactory.parseString(
+ """
+ |kamon.metric {
+ | tick-interval = 1 hour
+ | default-collection-context-buffer-size = 100
+ |}
+ """.stripMargin)
+
+ "the Kamon Annotation module" should {
+ "create a new trace when is invoked a static method annotated with @Trace" in {
+ for (id ← 1 to 10) AnnotatedJavaClass.trace()
+
+ val snapshot = takeSnapshotOf("trace", "trace")
+ snapshot.histogram("elapsed-time").get.numberOfMeasurements should be(10)
+ }
+ "create a segment when is invoked a static method annotated with @Segment" in {
+ for (id ← 1 to 7) AnnotatedJavaClass.segment()
+
+ val segmentMetricsSnapshot = takeSnapshotOf("inner-segment", "trace-segment",
+ tags = Map(
+ "trace" -> "trace-with-segment",
+ "category" -> "inner",
+ "library" -> "segment"))
+
+ segmentMetricsSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(7)
+ }
+
+ "create a segment when is invoked a static method annotated with @Segment and evaluate EL expressions" in {
+ for (id ← 1 to 10) AnnotatedJavaClass.segmentWithEL()
+
+ val snapshot = takeSnapshotOf("trace-with-segment-el", "trace")
+ snapshot.histogram("elapsed-time").get.numberOfMeasurements should be(10)
+
+ val segmentMetricsSnapshot = takeSnapshotOf("inner-segment:10", "trace-segment",
+ tags = Map(
+ "trace" -> "trace-with-segment-el",
+ "category" -> "segments",
+ "library" -> "segment"))
+
+ segmentMetricsSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(10)
+ }
+
+ "count the invocations of a static method annotated with @Count" in {
+ for (id ← 1 to 10) AnnotatedJavaClass.count()
+
+ val snapshot = takeSnapshotOf("count", "counter")
+ snapshot.counter("counter").get.count should be(10)
+ }
+
+ "count the invocations of a static method annotated with @Count and evaluate EL expressions" in {
+ for (id ← 1 to 2) AnnotatedJavaClass.countWithEL()
+
+ val snapshot = takeSnapshotOf("count:10", "counter", tags = Map("counter" -> "1", "env" -> "prod"))
+ snapshot.counter("counter").get.count should be(2)
+ }
+
+ "count the current invocations of a static method annotated with @MinMaxCount" in {
+ for (id ← 1 to 10) {
+ AnnotatedJavaClass.countMinMax()
+ }
+
+ val snapshot = takeSnapshotOf("minMax", "min-max-counter")
+ snapshot.minMaxCounter("min-max-counter").get.max should be(1)
+ }
+
+ "count the current invocations of a static method annotated with @MinMaxCount and evaluate EL expressions" in {
+ for (id ← 1 to 10) AnnotatedJavaClass.countMinMaxWithEL()
+
+ val snapshot = takeSnapshotOf("minMax:10", "min-max-counter", tags = Map("minMax" -> "1", "env" -> "dev"))
+ snapshot.minMaxCounter("min-max-counter").get.max should be(1)
+ }
+
+ "measure the time spent in the execution of a static method annotated with @Time" in {
+ for (id ← 1 to 1) AnnotatedJavaClass.time()
+
+ val snapshot = takeSnapshotOf("time", "histogram")
+ snapshot.histogram("histogram").get.numberOfMeasurements should be(1)
+ }
+
+ "measure the time spent in the execution of a static method annotated with @Time and evaluate EL expressions" in {
+ for (id ← 1 to 1) AnnotatedJavaClass.timeWithEL()
+
+ val snapshot = takeSnapshotOf("time:10", "histogram", tags = Map("slow-service" -> "service", "env" -> "prod"))
+ snapshot.histogram("histogram").get.numberOfMeasurements should be(1)
+ }
+
+ "record the value returned by a static method annotated with @Histogram" in {
+ for (value ← 1 to 5) AnnotatedJavaClass.histogram(value.toLong)
+
+ val snapshot = takeSnapshotOf("histogram", "histogram")
+ snapshot.histogram("histogram").get.numberOfMeasurements should be(5)
+ snapshot.histogram("histogram").get.min should be(1)
+ snapshot.histogram("histogram").get.max should be(5)
+ snapshot.histogram("histogram").get.sum should be(15)
+ }
+
+ "record the value returned by a static method annotated with @Histogram and evaluate EL expressions" in {
+ for (value ← 1 to 2) AnnotatedJavaClass.histogramWithEL(value.toLong)
+
+ val snapshot = takeSnapshotOf("histogram:10", "histogram", tags = Map("histogram" -> "hdr", "env" -> "prod"))
+ snapshot.histogram("histogram").get.numberOfMeasurements should be(2)
+ snapshot.histogram("histogram").get.min should be(1)
+ snapshot.histogram("histogram").get.max should be(2)
+ }
+ }
+} \ No newline at end of file
diff --git a/kamon-annotation/src/test/scala/kamon/annotation/StaticAnnotationInstrumentationSpec.scala b/kamon-annotation/src/test/scala/kamon/annotation/StaticAnnotationInstrumentationSpec.scala
new file mode 100644
index 00000000..a8e68009
--- /dev/null
+++ b/kamon-annotation/src/test/scala/kamon/annotation/StaticAnnotationInstrumentationSpec.scala
@@ -0,0 +1,188 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.annotation
+
+import com.typesafe.config.ConfigFactory
+import kamon.metric.{ HistogramKey, MinMaxCounterKey, CounterKey }
+import kamon.testkit.BaseKamonSpec
+
+class StaticAnnotationInstrumentationSpec extends BaseKamonSpec("static-annotation-instrumentation-spec") {
+ override lazy val config =
+ ConfigFactory.parseString(
+ """
+ |kamon.metric {
+ | tick-interval = 1 hour
+ | default-collection-context-buffer-size = 100
+ |}
+ """.stripMargin)
+ "the Kamon Annotation module" should {
+ "create a new trace when is invoked a method annotated with @Trace in a Scala Object" in {
+ for (id ← 1 to 42) AnnotatedObject.trace()
+
+ val snapshot = takeSnapshotOf("trace", "trace")
+ snapshot.histogram("elapsed-time").get.numberOfMeasurements should be(42)
+ }
+
+ "create a segment when is invoked a method annotated with @Trace and @Segment in a Scala Object" in {
+ for (id ← 1 to 15) AnnotatedObject.segment()
+
+ val segmentMetricsSnapshot = takeSnapshotOf("segment", "trace-segment",
+ tags = Map(
+ "trace" -> "trace-with-segment",
+ "category" -> "segments",
+ "library" -> "segment"))
+
+ segmentMetricsSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(15)
+ }
+
+ "create a segment when is invoked a method annotated with @Trace and @Segment and evaluate EL expressions in a Scala Object" in {
+ for (id ← 1 to 18) AnnotatedObject.segmentWithEL()
+
+ val snapshot = takeSnapshotOf("trace-with-segment-el", "trace")
+ snapshot.histogram("elapsed-time").get.numberOfMeasurements should be(18)
+
+ val segment10Snapshot = takeSnapshotOf("segment:10", "trace-segment",
+ tags = Map(
+ "trace" -> "trace-with-segment-el",
+ "category" -> "segments",
+ "library" -> "segment"))
+
+ segment10Snapshot.histogram("elapsed-time").get.numberOfMeasurements should be(18)
+
+ val innerSegmentSnapshot = takeSnapshotOf("inner-segment", "trace-segment",
+ tags = Map(
+ "trace" -> "trace-with-segment-el",
+ "category" -> "inner",
+ "library" -> "segment"))
+
+ innerSegmentSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(18)
+ }
+
+ "count the invocations of a method annotated with @Count in a Scala Object" in {
+ for (id ← 1 to 10) AnnotatedObject.count()
+
+ val snapshot = takeSnapshotOf("count", "counter")
+ snapshot.counter("counter").get.count should be(10)
+ }
+
+ "count the invocations of a method annotated with @Count and evaluate EL expressions in a Scala Object" in {
+ for (id ← 1 to 2) AnnotatedObject.countWithEL()
+
+ val snapshot = takeSnapshotOf("count:10", "counter", tags = Map("counter" -> "1", "env" -> "prod"))
+ snapshot.counter("counter").get.count should be(2)
+
+ }
+
+ "count the current invocations of a method annotated with @MinMaxCount in a Scala Object" in {
+ for (id ← 1 to 10) {
+ AnnotatedObject.countMinMax()
+ }
+
+ val snapshot = takeSnapshotOf("minMax", "min-max-counter")
+ snapshot.minMaxCounter("min-max-counter").get.max should be(1)
+ }
+
+ "count the current invocations of a method annotated with @MinMaxCount and evaluate EL expressions in a Scala Object" in {
+ for (id ← 1 to 10) AnnotatedObject.countMinMaxWithEL()
+
+ val snapshot = takeSnapshotOf("minMax:10", "min-max-counter", tags = Map("minMax" -> "1", "env" -> "dev"))
+ snapshot.minMaxCounter("min-max-counter").get.max should be(1)
+
+ }
+
+ "measure the time spent in the execution of a method annotated with @Time in a Scala Object" in {
+ for (id ← 1 to 1) AnnotatedObject.time()
+
+ val snapshot = takeSnapshotOf("time", "histogram")
+ snapshot.histogram("histogram").get.numberOfMeasurements should be(1)
+ }
+
+ "measure the time spent in the execution of a method annotated with @Time and evaluate EL expressions in a Scala Object" in {
+ for (id ← 1 to 1) AnnotatedObject.timeWithEL()
+
+ val snapshot = takeSnapshotOf("time:10", "histogram", tags = Map("slow-service" -> "service", "env" -> "prod"))
+ snapshot.histogram("histogram").get.numberOfMeasurements should be(1)
+ }
+
+ "record the value returned by a method annotated with @Histogram in a Scala Object" in {
+ for (value ← 1 to 5) AnnotatedObject.histogram(value)
+
+ val snapshot = takeSnapshotOf("histogram", "histogram")
+ snapshot.histogram("histogram").get.numberOfMeasurements should be(5)
+ snapshot.histogram("histogram").get.min should be(1)
+ snapshot.histogram("histogram").get.max should be(5)
+ snapshot.histogram("histogram").get.sum should be(15)
+ }
+
+ "record the value returned by a method annotated with @Histogram and evaluate EL expressions in a Scala Object" in {
+ for (value ← 1 to 2) AnnotatedObject.histogramWithEL(value)
+
+ val snapshot = takeSnapshotOf("histogram:10", "histogram", tags = Map("histogram" -> "hdr", "env" -> "prod"))
+ snapshot.histogram("histogram").get.numberOfMeasurements should be(2)
+ snapshot.histogram("histogram").get.min should be(1)
+ snapshot.histogram("histogram").get.max should be(2)
+ }
+ }
+}
+
+@EnableKamon
+object AnnotatedObject {
+
+ val Id = "10"
+
+ @Trace("trace")
+ def trace(): Unit = {}
+
+ @Trace("trace-with-segment")
+ @Segment(name = "segment", category = "segments", library = "segment")
+ def segment(): Unit = {
+ inner() // method annotated with @Segment
+ }
+
+ @Trace("trace-with-segment-el")
+ @Segment(name = "#{'segment:' += AnnotatedObject$.MODULE$.Id}", category = "segments", library = "segment")
+ def segmentWithEL(): Unit = {
+ inner() // method annotated with @Segment
+ }
+
+ @Count(name = "count")
+ def count(): Unit = {}
+
+ @Count(name = "${'count:' += AnnotatedObject$.MODULE$.Id}", tags = "${'counter':'1', 'env':'prod'}")
+ def countWithEL(): Unit = {}
+
+ @MinMaxCount(name = "minMax")
+ def countMinMax(): Unit = {}
+
+ @MinMaxCount(name = "#{'minMax:' += AnnotatedObject$.MODULE$.Id}", tags = "#{'minMax':'1', 'env':'dev'}")
+ def countMinMaxWithEL(): Unit = {}
+
+ @Time(name = "time")
+ def time(): Unit = {}
+
+ @Time(name = "${'time:' += AnnotatedObject$.MODULE$.Id}", tags = "${'slow-service':'service', 'env':'prod'}")
+ def timeWithEL(): Unit = {}
+
+ @Histogram(name = "histogram")
+ def histogram(value: Long): Long = value
+
+ @Histogram(name = "#{'histogram:' += AnnotatedObject$.MODULE$.Id}", tags = "${'histogram':'hdr', 'env':'prod'}")
+ def histogramWithEL(value: Long): Long = value
+
+ @Segment(name = "inner-segment", category = "inner", library = "segment")
+ private def inner(): Unit = {}
+} \ No newline at end of file
diff --git a/kamon-core/src/main/resources/META-INF/aop.xml b/kamon-core/src/main/resources/META-INF/aop.xml
index 2ffb8b09..b13f9aac 100644
--- a/kamon-core/src/main/resources/META-INF/aop.xml
+++ b/kamon-core/src/main/resources/META-INF/aop.xml
@@ -4,7 +4,7 @@
<aspects>
<!-- Notify that AspectJ is present -->
- <aspect name="kamon.supervisor.AspectJPresent"/>
+ <aspect name="kamon.AspectJPresent"/>
</aspects>
diff --git a/kamon-core/src/main/scala/kamon/Kamon.scala b/kamon-core/src/main/scala/kamon/Kamon.scala
index a2a24f49..f8253875 100644
--- a/kamon-core/src/main/scala/kamon/Kamon.scala
+++ b/kamon-core/src/main/scala/kamon/Kamon.scala
@@ -18,15 +18,13 @@ import _root_.akka.actor
import _root_.akka.actor._
import com.typesafe.config.{ ConfigFactory, Config }
import kamon.metric._
+import kamon.metric.instrument.Gauge
import kamon.trace.{ TracerImpl, Tracer }
object Kamon {
trait Extension extends actor.Extension
- private case class KamonCoreComponents(
- metrics: Metrics,
- tracer: Tracer,
- simpleMetrics: SimpleMetrics)
+ private case class KamonCoreComponents(metrics: Metrics, tracer: Tracer)
@volatile private var _system: ActorSystem = _
@volatile private var _coreComponents: Option[KamonCoreComponents] = None
@@ -43,14 +41,14 @@ object Kamon {
if (_coreComponents.isEmpty) {
val metrics = MetricsImpl(config)
- val simpleMetrics = SimpleMetricsImpl(metrics)
val tracer = TracerImpl(metrics, config)
- _coreComponents = Some(KamonCoreComponents(metrics, tracer, simpleMetrics))
+ _coreComponents = Some(KamonCoreComponents(metrics, tracer))
_system = ActorSystem("kamon", resolveInternalConfig)
metrics.start(_system)
tracer.start(_system)
+ _system.registerExtension(ModuleLoader)
} else sys.error("Kamon has already been started.")
}
@@ -70,9 +68,6 @@ object Kamon {
def tracer: Tracer =
ifStarted(_.tracer)
- def simpleMetrics: SimpleMetrics =
- ifStarted(_.simpleMetrics)
-
def apply[T <: Kamon.Extension](key: ExtensionId[T]): T =
ifStarted { _ ⇒
if (_system ne null)
diff --git a/kamon-core/src/main/scala/kamon/ModuleLoader.scala b/kamon-core/src/main/scala/kamon/ModuleLoader.scala
new file mode 100644
index 00000000..d1b7466e
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/ModuleLoader.scala
@@ -0,0 +1,123 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon
+
+import _root_.akka.actor
+import _root_.akka.actor._
+import _root_.akka.event.Logging
+import org.aspectj.lang.ProceedingJoinPoint
+import org.aspectj.lang.annotation.{ Around, Pointcut, Aspect }
+
+private[kamon] object ModuleLoader extends ExtensionId[ModuleLoaderExtension] with ExtensionIdProvider {
+ def lookup(): ExtensionId[_ <: actor.Extension] = ModuleLoader
+ def createExtension(system: ExtendedActorSystem): ModuleLoaderExtension = new ModuleLoaderExtension(system)
+}
+
+private[kamon] class ModuleLoaderExtension(system: ExtendedActorSystem) extends Kamon.Extension {
+ val log = Logging(system, "ModuleLoader")
+ val settings = ModuleLoaderSettings(system)
+
+ if (settings.modulesRequiringAspectJ.nonEmpty && !isAspectJPresent && settings.showAspectJMissingWarning)
+ logAspectJWeaverMissing(settings.modulesRequiringAspectJ)
+
+ // Force initialization of all modules marked with auto-start.
+ settings.availableModules.filter(_.autoStart).foreach { module ⇒
+ if (module.extensionClass == "none")
+ log.debug("Ignoring auto start of the [{}] module with no extension class.")
+ else
+ system.dynamicAccess.getObjectFor[ExtensionId[Kamon.Extension]](module.extensionClass).map { moduleID ⇒
+ log.debug("Auto starting the [{}] module.", module.name)
+ moduleID.get(system)
+
+ } recover {
+ case th: Throwable ⇒ log.error(th, "Failed to auto start the [{}] module.", module.name)
+ }
+
+ }
+
+ // When AspectJ is present the kamon.supervisor.AspectJPresent aspect will make this return true.
+ def isAspectJPresent: Boolean = false
+
+ def logAspectJWeaverMissing(modulesRequiringAspectJ: List[AvailableModuleInfo]): Unit = {
+ val moduleNames = modulesRequiringAspectJ.map(_.name).mkString(", ")
+ val weaverMissingMessage =
+ """
+ |
+ | ___ _ ___ _ _ ___ ___ _ _
+ | / _ \ | | |_ | | | | | | \/ |(_) (_)
+ |/ /_\ \ ___ _ __ ___ ___ | |_ | | | | | | ___ __ _ __ __ ___ _ __ | . . | _ ___ ___ _ _ __ __ _
+ || _ |/ __|| '_ \ / _ \ / __|| __| | | | |/\| | / _ \ / _` |\ \ / // _ \| '__| | |\/| || |/ __|/ __|| || '_ \ / _` |
+ || | | |\__ \| |_) || __/| (__ | |_ /\__/ / \ /\ /| __/| (_| | \ V /| __/| | | | | || |\__ \\__ \| || | | || (_| |
+ |\_| |_/|___/| .__/ \___| \___| \__|\____/ \/ \/ \___| \__,_| \_/ \___||_| \_| |_/|_||___/|___/|_||_| |_| \__, |
+ | | | __/ |
+ | |_| |___/
+ |
+ | It seems like your application was not started with the -javaagent:/path-to-aspectj-weaver.jar option but Kamon detected
+ | the following modules which require AspecJ to work properly:
+ |
+ """.stripMargin + moduleNames +
+ """
+ |
+ | If you need help on setting up the aspectj weaver go to http://kamon.io/introduction/get-started/ for more info. On the
+ | other hand, if you are sure that you do not need or do not want to use the weaver then you can disable this error message
+ | by changing the kamon.show-aspectj-missing-warning setting in your configuration file.
+ |
+ """.stripMargin
+
+ log.error(weaverMissingMessage)
+ }
+}
+
+private[kamon] case class AvailableModuleInfo(name: String, extensionClass: String, requiresAspectJ: Boolean, autoStart: Boolean)
+private[kamon] case class ModuleLoaderSettings(showAspectJMissingWarning: Boolean, availableModules: List[AvailableModuleInfo]) {
+ val modulesRequiringAspectJ = availableModules.filter(_.requiresAspectJ)
+}
+
+private[kamon] object ModuleLoaderSettings {
+
+ def apply(system: ActorSystem): ModuleLoaderSettings = {
+ import kamon.util.ConfigTools.Syntax
+
+ val config = system.settings.config.getConfig("kamon.modules")
+ val showAspectJMissingWarning = system.settings.config.getBoolean("kamon.show-aspectj-missing-warning")
+
+ val modules = config.firstLevelKeys
+ val availableModules = modules.map { moduleName ⇒
+ val moduleConfig = config.getConfig(moduleName)
+
+ AvailableModuleInfo(
+ moduleName,
+ moduleConfig.getString("extension-id"),
+ moduleConfig.getBoolean("requires-aspectj"),
+ moduleConfig.getBoolean("auto-start"))
+
+ } toList
+
+ ModuleLoaderSettings(showAspectJMissingWarning, availableModules)
+ }
+}
+
+@Aspect
+private[kamon] class AspectJPresent {
+
+ @Pointcut("execution(* kamon.ModuleLoaderExtension.isAspectJPresent())")
+ def isAspectJPresentAtModuleSupervisor(): Unit = {}
+
+ @Around("isAspectJPresentAtModuleSupervisor()")
+ def aroundIsAspectJPresentAtModuleSupervisor(pjp: ProceedingJoinPoint): Boolean = true
+
+}
diff --git a/kamon-core/src/main/scala/kamon/metric/Entity.scala b/kamon-core/src/main/scala/kamon/metric/Entity.scala
index 8d328f83..91249af0 100644
--- a/kamon-core/src/main/scala/kamon/metric/Entity.scala
+++ b/kamon-core/src/main/scala/kamon/metric/Entity.scala
@@ -23,36 +23,15 @@ package kamon.metric
*
* // TODO: Find a better word for `thing`.
*/
-class Entity(val name: String, val category: String, val metadata: Map[String, String]) {
-
- override def equals(o: Any): Boolean = {
- if (this eq o.asInstanceOf[AnyRef])
- true
- else if ((o.asInstanceOf[AnyRef] eq null) || !o.isInstanceOf[Entity])
- false
- else {
- val thatAsEntity = o.asInstanceOf[Entity]
- category == thatAsEntity.category && name == thatAsEntity.name
- }
- }
-
- override def hashCode: Int = {
- var result: Int = name.hashCode
- result = 31 * result + category.hashCode
- return result
- }
-}
+case class Entity(name: String, category: String, tags: Map[String, String])
object Entity {
def apply(name: String, category: String): Entity =
apply(name, category, Map.empty)
- def apply(name: String, category: String, metadata: Map[String, String]): Entity =
- new Entity(name, category, metadata)
-
def create(name: String, category: String): Entity =
apply(name, category, Map.empty)
- def create(name: String, category: String, metadata: Map[String, String]): Entity =
- new Entity(name, category, metadata)
+ def create(name: String, category: String, tags: Map[String, String]): Entity =
+ new Entity(name, category, tags)
} \ No newline at end of file
diff --git a/kamon-core/src/main/scala/kamon/metric/EntityRecorder.scala b/kamon-core/src/main/scala/kamon/metric/EntityRecorder.scala
index 6e0a4248..65dafa9a 100644
--- a/kamon-core/src/main/scala/kamon/metric/EntityRecorder.scala
+++ b/kamon-core/src/main/scala/kamon/metric/EntityRecorder.scala
@@ -19,6 +19,7 @@ package kamon.metric
import kamon.metric.instrument.Gauge.CurrentValueCollector
import kamon.metric.instrument.Histogram.DynamicRange
import kamon.metric.instrument._
+import kamon.util.Function
import scala.collection.concurrent.TrieMap
import scala.concurrent.duration.FiniteDuration
@@ -33,6 +34,64 @@ trait EntityRecorderFactory[T <: EntityRecorder] {
def createRecorder(instrumentFactory: InstrumentFactory): T
}
+object EntityRecorderFactory {
+ def apply[T <: EntityRecorder](entityCategory: String, factory: InstrumentFactory ⇒ T): EntityRecorderFactory[T] =
+ new EntityRecorderFactory[T] {
+ def category: String = entityCategory
+ def createRecorder(instrumentFactory: InstrumentFactory): T = factory(instrumentFactory)
+ }
+
+ def create[T <: EntityRecorder](entityCategory: String, factory: Function[InstrumentFactory, T]): EntityRecorderFactory[T] =
+ new EntityRecorderFactory[T] {
+ def category: String = entityCategory
+ def createRecorder(instrumentFactory: InstrumentFactory): T = factory(instrumentFactory)
+ }
+}
+
+private[kamon] sealed trait SingleInstrumentEntityRecorder extends EntityRecorder {
+ def key: MetricKey
+ def instrument: Instrument
+
+ def collect(collectionContext: CollectionContext): EntitySnapshot =
+ new DefaultEntitySnapshot(Map(key -> instrument.collect(collectionContext)))
+
+ def cleanup: Unit = instrument.cleanup
+}
+
+object SingleInstrumentEntityRecorder {
+ val Histogram = "histogram"
+ val MinMaxCounter = "min-max-counter"
+ val Gauge = "gauge"
+ val Counter = "counter"
+
+ val AllCategories = List("histogram", "gauge", "counter", "min-max-counter")
+}
+
+/**
+ * Entity recorder for a single Counter instrument.
+ */
+case class CounterRecorder(key: MetricKey, instrument: Counter) extends SingleInstrumentEntityRecorder
+
+/**
+ * Entity recorder for a single Histogram instrument.
+ */
+case class HistogramRecorder(key: MetricKey, instrument: Histogram) extends SingleInstrumentEntityRecorder
+
+/**
+ * Entity recorder for a single MinMaxCounter instrument.
+ */
+case class MinMaxCounterRecorder(key: MetricKey, instrument: MinMaxCounter) extends SingleInstrumentEntityRecorder
+
+/**
+ * Entity recorder for a single Gauge instrument.
+ */
+case class GaugeRecorder(key: MetricKey, instrument: Gauge) extends SingleInstrumentEntityRecorder
+
+/**
+ * Base class with plenty of utility methods to facilitate the creation of [[EntityRecorder]] implementations.
+ * It is not required to use this base class for defining custom a custom [[EntityRecorder]], but it is certainly
+ * the most convenient way to do it and the preferred approach throughout the Kamon codebase.
+ */
abstract class GenericEntityRecorder(instrumentFactory: InstrumentFactory) extends EntityRecorder {
import kamon.util.TriemapAtomicGetOrElseUpdate.Syntax
@@ -41,10 +100,10 @@ abstract class GenericEntityRecorder(instrumentFactory: InstrumentFactory) exten
_instruments.atomicGetOrElseUpdate(key, instrument, _.cleanup).asInstanceOf[T]
protected def histogram(name: String): Histogram =
- register(HistogramKey(name), instrumentFactory.createHistogram(name))
+ register(HistogramKey(name, UnitOfMeasurement.Unknown), instrumentFactory.createHistogram(name))
protected def histogram(name: String, dynamicRange: DynamicRange): Histogram =
- register(HistogramKey(name), instrumentFactory.createHistogram(name, Some(dynamicRange)))
+ register(HistogramKey(name, UnitOfMeasurement.Unknown), instrumentFactory.createHistogram(name, Some(dynamicRange)))
protected def histogram(name: String, unitOfMeasurement: UnitOfMeasurement): Histogram =
register(HistogramKey(name, unitOfMeasurement), instrumentFactory.createHistogram(name))
@@ -52,32 +111,26 @@ abstract class GenericEntityRecorder(instrumentFactory: InstrumentFactory) exten
protected def histogram(name: String, dynamicRange: DynamicRange, unitOfMeasurement: UnitOfMeasurement): Histogram =
register(HistogramKey(name, unitOfMeasurement), instrumentFactory.createHistogram(name, Some(dynamicRange)))
- protected def histogram(key: HistogramKey): Histogram =
- register(key, instrumentFactory.createHistogram(key.name))
-
- protected def histogram(key: HistogramKey, dynamicRange: DynamicRange): Histogram =
- register(key, instrumentFactory.createHistogram(key.name, Some(dynamicRange)))
-
protected def removeHistogram(name: String): Unit =
- _instruments.remove(HistogramKey(name))
+ _instruments.remove(HistogramKey(name, UnitOfMeasurement.Unknown))
- protected def removeHistogram(key: HistogramKey): Unit =
- _instruments.remove(key)
+ protected def removeHistogram(name: String, unitOfMeasurement: UnitOfMeasurement): Unit =
+ _instruments.remove(HistogramKey(name, unitOfMeasurement))
protected def minMaxCounter(name: String): MinMaxCounter =
- register(MinMaxCounterKey(name), instrumentFactory.createMinMaxCounter(name))
+ register(MinMaxCounterKey(name, UnitOfMeasurement.Unknown), instrumentFactory.createMinMaxCounter(name))
protected def minMaxCounter(name: String, dynamicRange: DynamicRange): MinMaxCounter =
- register(MinMaxCounterKey(name), instrumentFactory.createMinMaxCounter(name, Some(dynamicRange)))
+ register(MinMaxCounterKey(name, UnitOfMeasurement.Unknown), instrumentFactory.createMinMaxCounter(name, Some(dynamicRange)))
protected def minMaxCounter(name: String, refreshInterval: FiniteDuration): MinMaxCounter =
- register(MinMaxCounterKey(name), instrumentFactory.createMinMaxCounter(name, refreshInterval = Some(refreshInterval)))
+ register(MinMaxCounterKey(name, UnitOfMeasurement.Unknown), instrumentFactory.createMinMaxCounter(name, refreshInterval = Some(refreshInterval)))
protected def minMaxCounter(name: String, unitOfMeasurement: UnitOfMeasurement): MinMaxCounter =
register(MinMaxCounterKey(name, unitOfMeasurement), instrumentFactory.createMinMaxCounter(name))
protected def minMaxCounter(name: String, dynamicRange: DynamicRange, refreshInterval: FiniteDuration): MinMaxCounter =
- register(MinMaxCounterKey(name), instrumentFactory.createMinMaxCounter(name, Some(dynamicRange), Some(refreshInterval)))
+ register(MinMaxCounterKey(name, UnitOfMeasurement.Unknown), instrumentFactory.createMinMaxCounter(name, Some(dynamicRange), Some(refreshInterval)))
protected def minMaxCounter(name: String, dynamicRange: DynamicRange, unitOfMeasurement: UnitOfMeasurement): MinMaxCounter =
register(MinMaxCounterKey(name, unitOfMeasurement), instrumentFactory.createMinMaxCounter(name, Some(dynamicRange)))
@@ -86,7 +139,7 @@ abstract class GenericEntityRecorder(instrumentFactory: InstrumentFactory) exten
register(MinMaxCounterKey(name, unitOfMeasurement), instrumentFactory.createMinMaxCounter(name, refreshInterval = Some(refreshInterval)))
protected def minMaxCounter(name: String, dynamicRange: DynamicRange, refreshInterval: FiniteDuration, unitOfMeasurement: UnitOfMeasurement): MinMaxCounter =
- register(MinMaxCounterKey(name), instrumentFactory.createMinMaxCounter(name, Some(dynamicRange), Some(refreshInterval)))
+ register(MinMaxCounterKey(name, UnitOfMeasurement.Unknown), instrumentFactory.createMinMaxCounter(name, Some(dynamicRange), Some(refreshInterval)))
protected def minMaxCounter(key: MinMaxCounterKey): MinMaxCounter =
register(key, instrumentFactory.createMinMaxCounter(key.name))
@@ -101,31 +154,31 @@ abstract class GenericEntityRecorder(instrumentFactory: InstrumentFactory) exten
register(key, instrumentFactory.createMinMaxCounter(key.name, Some(dynamicRange), Some(refreshInterval)))
protected def removeMinMaxCounter(name: String): Unit =
- _instruments.remove(MinMaxCounterKey(name))
+ _instruments.remove(MinMaxCounterKey(name, UnitOfMeasurement.Unknown))
protected def removeMinMaxCounter(key: MinMaxCounterKey): Unit =
_instruments.remove(key)
protected def gauge(name: String, valueCollector: CurrentValueCollector): Gauge =
- register(GaugeKey(name), instrumentFactory.createGauge(name, valueCollector = valueCollector))
+ register(GaugeKey(name, UnitOfMeasurement.Unknown), instrumentFactory.createGauge(name, valueCollector = valueCollector))
protected def gauge(name: String, dynamicRange: DynamicRange, valueCollector: CurrentValueCollector): Gauge =
- register(GaugeKey(name), instrumentFactory.createGauge(name, Some(dynamicRange), valueCollector = valueCollector))
+ register(GaugeKey(name, UnitOfMeasurement.Unknown), instrumentFactory.createGauge(name, Some(dynamicRange), valueCollector = valueCollector))
protected def gauge(name: String, refreshInterval: FiniteDuration, valueCollector: CurrentValueCollector): Gauge =
- register(GaugeKey(name), instrumentFactory.createGauge(name, refreshInterval = Some(refreshInterval), valueCollector = valueCollector))
+ register(GaugeKey(name, UnitOfMeasurement.Unknown), instrumentFactory.createGauge(name, refreshInterval = Some(refreshInterval), valueCollector = valueCollector))
protected def gauge(name: String, unitOfMeasurement: UnitOfMeasurement, valueCollector: CurrentValueCollector): Gauge =
register(GaugeKey(name, unitOfMeasurement), instrumentFactory.createGauge(name, valueCollector = valueCollector))
protected def gauge(name: String, dynamicRange: DynamicRange, refreshInterval: FiniteDuration, valueCollector: CurrentValueCollector): Gauge =
- register(GaugeKey(name), instrumentFactory.createGauge(name, Some(dynamicRange), Some(refreshInterval), valueCollector = valueCollector))
+ register(GaugeKey(name, UnitOfMeasurement.Unknown), instrumentFactory.createGauge(name, Some(dynamicRange), Some(refreshInterval), valueCollector = valueCollector))
protected def gauge(name: String, dynamicRange: DynamicRange, unitOfMeasurement: UnitOfMeasurement, valueCollector: CurrentValueCollector): Gauge =
register(GaugeKey(name, unitOfMeasurement), instrumentFactory.createGauge(name, Some(dynamicRange), valueCollector = valueCollector))
protected def gauge(name: String, refreshInterval: FiniteDuration, unitOfMeasurement: UnitOfMeasurement, valueCollector: CurrentValueCollector): Gauge =
- register(GaugeKey(name), instrumentFactory.createGauge(name, refreshInterval = Some(refreshInterval), valueCollector = valueCollector))
+ register(GaugeKey(name, UnitOfMeasurement.Unknown), instrumentFactory.createGauge(name, refreshInterval = Some(refreshInterval), valueCollector = valueCollector))
protected def gauge(name: String, dynamicRange: DynamicRange, refreshInterval: FiniteDuration, unitOfMeasurement: UnitOfMeasurement, valueCollector: CurrentValueCollector): Gauge =
register(GaugeKey(name, unitOfMeasurement), instrumentFactory.createGauge(name, Some(dynamicRange), Some(refreshInterval), valueCollector))
@@ -143,19 +196,19 @@ abstract class GenericEntityRecorder(instrumentFactory: InstrumentFactory) exten
register(key, instrumentFactory.createGauge(key.name, Some(dynamicRange), Some(refreshInterval), valueCollector = valueCollector))
protected def removeGauge(name: String): Unit =
- _instruments.remove(GaugeKey(name))
+ _instruments.remove(GaugeKey(name, UnitOfMeasurement.Unknown))
protected def removeGauge(key: GaugeKey): Unit =
_instruments.remove(key)
protected def counter(name: String): Counter =
- register(CounterKey(name), instrumentFactory.createCounter())
+ register(CounterKey(name, UnitOfMeasurement.Unknown), instrumentFactory.createCounter())
protected def counter(key: CounterKey): Counter =
register(key, instrumentFactory.createCounter())
protected def removeCounter(name: String): Unit =
- _instruments.remove(CounterKey(name))
+ _instruments.remove(CounterKey(name, UnitOfMeasurement.Unknown))
protected def removeCounter(key: CounterKey): Unit =
_instruments.remove(key)
diff --git a/kamon-core/src/main/scala/kamon/metric/MetricKey.scala b/kamon-core/src/main/scala/kamon/metric/MetricKey.scala
index a5d30c81..0d4e0163 100644
--- a/kamon-core/src/main/scala/kamon/metric/MetricKey.scala
+++ b/kamon-core/src/main/scala/kamon/metric/MetricKey.scala
@@ -16,154 +16,32 @@
package kamon.metric
-import kamon.metric.instrument.{ InstrumentTypes, InstrumentType, UnitOfMeasurement }
+import kamon.metric.instrument.UnitOfMeasurement
/**
- * MetricKeys are used to identify a given metric in entity recorders and snapshots. MetricKeys can be used to encode
- * additional metadata for a metric being recorded, as well as the unit of measurement of the data being recorder.
+ * MetricKeys are used to identify a given metric in entity recorders and snapshots.
*/
sealed trait MetricKey {
def name: String
def unitOfMeasurement: UnitOfMeasurement
- def instrumentType: InstrumentType
- def metadata: Map[String, String]
}
-// Wish that there was a shorter way to describe the operations bellow, but apparently there is no way to generalize all
-// the apply/create versions that would produce the desired return types when used from Java.
-
/**
* MetricKey for all Histogram-based metrics.
*/
-case class HistogramKey(name: String, unitOfMeasurement: UnitOfMeasurement, metadata: Map[String, String]) extends MetricKey {
- val instrumentType = InstrumentTypes.Histogram
-}
-
-object HistogramKey {
- def apply(name: String): HistogramKey =
- apply(name, UnitOfMeasurement.Unknown)
-
- def apply(name: String, unitOfMeasurement: UnitOfMeasurement): HistogramKey =
- apply(name, unitOfMeasurement, Map.empty)
-
- def apply(name: String, metadata: Map[String, String]): HistogramKey =
- apply(name, UnitOfMeasurement.Unknown, Map.empty)
-
- /**
- * Java friendly versions:
- */
-
- def create(name: String): HistogramKey =
- apply(name, UnitOfMeasurement.Unknown)
-
- def create(name: String, unitOfMeasurement: UnitOfMeasurement): HistogramKey =
- apply(name, unitOfMeasurement)
-
- def create(name: String, metadata: Map[String, String]): HistogramKey =
- apply(name, metadata)
-
- def create(name: String, unitOfMeasurement: UnitOfMeasurement, metadata: Map[String, String]): HistogramKey =
- apply(name, unitOfMeasurement, metadata)
-}
+private[kamon] case class HistogramKey(name: String, unitOfMeasurement: UnitOfMeasurement) extends MetricKey
/**
* MetricKey for all MinMaxCounter-based metrics.
*/
-case class MinMaxCounterKey(name: String, unitOfMeasurement: UnitOfMeasurement, metadata: Map[String, String]) extends MetricKey {
- val instrumentType = InstrumentTypes.MinMaxCounter
-}
-
-object MinMaxCounterKey {
- def apply(name: String): MinMaxCounterKey =
- apply(name, UnitOfMeasurement.Unknown)
-
- def apply(name: String, unitOfMeasurement: UnitOfMeasurement): MinMaxCounterKey =
- apply(name, unitOfMeasurement, Map.empty)
-
- def apply(name: String, metadata: Map[String, String]): MinMaxCounterKey =
- apply(name, UnitOfMeasurement.Unknown, Map.empty)
-
- /**
- * Java friendly versions:
- */
-
- def create(name: String): MinMaxCounterKey =
- apply(name, UnitOfMeasurement.Unknown)
-
- def create(name: String, unitOfMeasurement: UnitOfMeasurement): MinMaxCounterKey =
- apply(name, unitOfMeasurement)
-
- def create(name: String, metadata: Map[String, String]): MinMaxCounterKey =
- apply(name, metadata)
-
- def create(name: String, unitOfMeasurement: UnitOfMeasurement, metadata: Map[String, String]): MinMaxCounterKey =
- apply(name, unitOfMeasurement, metadata)
-}
+private[kamon] case class MinMaxCounterKey(name: String, unitOfMeasurement: UnitOfMeasurement) extends MetricKey
/**
* MetricKey for all Gauge-based metrics.
*/
-case class GaugeKey(name: String, unitOfMeasurement: UnitOfMeasurement, metadata: Map[String, String]) extends MetricKey {
- val instrumentType = InstrumentTypes.Gauge
-}
-
-object GaugeKey {
- def apply(name: String): GaugeKey =
- apply(name, UnitOfMeasurement.Unknown)
-
- def apply(name: String, unitOfMeasurement: UnitOfMeasurement): GaugeKey =
- apply(name, unitOfMeasurement, Map.empty)
-
- def apply(name: String, metadata: Map[String, String]): GaugeKey =
- apply(name, UnitOfMeasurement.Unknown, Map.empty)
-
- /**
- * Java friendly versions:
- */
-
- def create(name: String): GaugeKey =
- apply(name, UnitOfMeasurement.Unknown)
-
- def create(name: String, unitOfMeasurement: UnitOfMeasurement): GaugeKey =
- apply(name, unitOfMeasurement)
-
- def create(name: String, metadata: Map[String, String]): GaugeKey =
- apply(name, metadata)
-
- def create(name: String, unitOfMeasurement: UnitOfMeasurement, metadata: Map[String, String]): GaugeKey =
- apply(name, unitOfMeasurement, metadata)
-}
+private[kamon] case class GaugeKey(name: String, unitOfMeasurement: UnitOfMeasurement) extends MetricKey
/**
* MetricKey for all Counter-based metrics.
*/
-case class CounterKey(name: String, unitOfMeasurement: UnitOfMeasurement, metadata: Map[String, String]) extends MetricKey {
- val instrumentType = InstrumentTypes.Counter
-}
-
-object CounterKey {
- def apply(name: String): CounterKey =
- apply(name, UnitOfMeasurement.Unknown)
-
- def apply(name: String, unitOfMeasurement: UnitOfMeasurement): CounterKey =
- apply(name, unitOfMeasurement, Map.empty)
-
- def apply(name: String, metadata: Map[String, String]): CounterKey =
- apply(name, UnitOfMeasurement.Unknown, Map.empty)
-
- /**
- * Java friendly versions:
- */
-
- def create(name: String): CounterKey =
- apply(name, UnitOfMeasurement.Unknown)
-
- def create(name: String, unitOfMeasurement: UnitOfMeasurement): CounterKey =
- apply(name, unitOfMeasurement)
-
- def create(name: String, metadata: Map[String, String]): CounterKey =
- apply(name, metadata)
-
- def create(name: String, unitOfMeasurement: UnitOfMeasurement, metadata: Map[String, String]): CounterKey =
- apply(name, unitOfMeasurement, metadata)
-} \ No newline at end of file
+private[kamon] case class CounterKey(name: String, unitOfMeasurement: UnitOfMeasurement) extends MetricKey
diff --git a/kamon-core/src/main/scala/kamon/metric/Metrics.scala b/kamon-core/src/main/scala/kamon/metric/Metrics.scala
index d79b1de3..9fd9e771 100644
--- a/kamon-core/src/main/scala/kamon/metric/Metrics.scala
+++ b/kamon-core/src/main/scala/kamon/metric/Metrics.scala
@@ -16,46 +16,221 @@
package kamon.metric
+import akka.actor._
import com.typesafe.config.Config
-import kamon.metric.SubscriptionsDispatcher.{ Unsubscribe, Subscribe }
-import kamon.metric.instrument.{ DefaultRefreshScheduler, InstrumentFactory, CollectionContext }
+import kamon.metric.SubscriptionsDispatcher.{ Subscribe, Unsubscribe }
+import kamon.metric.instrument.Gauge.CurrentValueCollector
+import kamon.metric.instrument.Histogram.DynamicRange
+import kamon.metric.instrument._
+import kamon.util.LazyActorRef
import scala.collection.concurrent.TrieMap
-import akka.actor._
-import kamon.util.{ LazyActorRef, TriemapAtomicGetOrElseUpdate }
+import scala.concurrent.duration.FiniteDuration
case class EntityRegistration[T <: EntityRecorder](entity: Entity, recorder: T)
trait Metrics {
def settings: MetricsSettings
+
def shouldTrack(entity: Entity): Boolean
+
def shouldTrack(entityName: String, category: String): Boolean =
shouldTrack(Entity(entityName, category))
- def register[T <: EntityRecorder](recorderFactory: EntityRecorderFactory[T], entityName: String): Option[EntityRegistration[T]]
- def register[T <: EntityRecorder](entity: Entity, recorder: T): EntityRegistration[T]
- def unregister(entity: Entity): Unit
+ //
+ // Histograms registration and removal
+ //
+
+ def histogram(name: String): Histogram =
+ registerHistogram(name)
+
+ def histogram(name: String, unitOfMeasurement: UnitOfMeasurement): Histogram =
+ registerHistogram(name, unitOfMeasurement = Some(unitOfMeasurement))
+
+ def histogram(name: String, dynamicRange: DynamicRange): Histogram =
+ registerHistogram(name, dynamicRange = Some(dynamicRange))
+
+ def histogram(name: String, unitOfMeasurement: UnitOfMeasurement, dynamicRange: DynamicRange): Histogram =
+ registerHistogram(name, unitOfMeasurement = Some(unitOfMeasurement), dynamicRange = Some(dynamicRange))
+
+ def histogram(name: String, tags: Map[String, String]): Histogram =
+ registerHistogram(name, tags)
+
+ def histogram(name: String, tags: Map[String, String], unitOfMeasurement: UnitOfMeasurement): Histogram =
+ registerHistogram(name, tags, Some(unitOfMeasurement))
+
+ def histogram(name: String, tags: Map[String, String], dynamicRange: DynamicRange): Histogram =
+ registerHistogram(name, tags, dynamicRange = Some(dynamicRange))
+
+ def histogram(name: String, tags: Map[String, String], unitOfMeasurement: UnitOfMeasurement, dynamicRange: DynamicRange): Histogram =
+ registerHistogram(name, tags, Some(unitOfMeasurement), Some(dynamicRange))
+
+ def removeHistogram(name: String): Boolean =
+ removeHistogram(name, Map.empty)
+
+ def registerHistogram(name: String, tags: Map[String, String] = Map.empty, unitOfMeasurement: Option[UnitOfMeasurement] = None,
+ dynamicRange: Option[DynamicRange] = None): Histogram
+
+ def removeHistogram(name: String, tags: Map[String, String]): Boolean
+
+ //
+ // MinMaxCounter registration and removal
+ //
+
+ def minMaxCounter(name: String): MinMaxCounter =
+ registerMinMaxCounter(name)
+
+ def minMaxCounter(name: String, unitOfMeasurement: UnitOfMeasurement): MinMaxCounter =
+ registerMinMaxCounter(name, unitOfMeasurement = Some(unitOfMeasurement))
+
+ def minMaxCounter(name: String, dynamicRange: DynamicRange): MinMaxCounter =
+ registerMinMaxCounter(name, dynamicRange = Some(dynamicRange))
+
+ def minMaxCounter(name: String, refreshInterval: FiniteDuration): MinMaxCounter =
+ registerMinMaxCounter(name, refreshInterval = Some(refreshInterval))
+
+ def minMaxCounter(name: String, dynamicRange: DynamicRange, refreshInterval: FiniteDuration): MinMaxCounter =
+ registerMinMaxCounter(name, dynamicRange = Some(dynamicRange), refreshInterval = Some(refreshInterval))
+
+ def minMaxCounter(name: String, unitOfMeasurement: UnitOfMeasurement, dynamicRange: DynamicRange): MinMaxCounter =
+ registerMinMaxCounter(name, unitOfMeasurement = Some(unitOfMeasurement), dynamicRange = Some(dynamicRange))
+
+ def minMaxCounter(name: String, tags: Map[String, String]): MinMaxCounter =
+ registerMinMaxCounter(name, tags)
+
+ def minMaxCounter(name: String, tags: Map[String, String], unitOfMeasurement: UnitOfMeasurement): MinMaxCounter =
+ registerMinMaxCounter(name, tags, Some(unitOfMeasurement))
+
+ def minMaxCounter(name: String, tags: Map[String, String], dynamicRange: DynamicRange): MinMaxCounter =
+ registerMinMaxCounter(name, tags, dynamicRange = Some(dynamicRange))
+
+ def minMaxCounter(name: String, tags: Map[String, String], unitOfMeasurement: UnitOfMeasurement, dynamicRange: DynamicRange): MinMaxCounter =
+ registerMinMaxCounter(name, tags, Some(unitOfMeasurement), Some(dynamicRange))
+
+ def removeMinMaxCounter(name: String): Boolean =
+ removeMinMaxCounter(name, Map.empty)
+
+ def removeMinMaxCounter(name: String, tags: Map[String, String]): Boolean
+
+ def registerMinMaxCounter(name: String, tags: Map[String, String] = Map.empty, unitOfMeasurement: Option[UnitOfMeasurement] = None,
+ dynamicRange: Option[DynamicRange] = None, refreshInterval: Option[FiniteDuration] = None): MinMaxCounter
+
+ //
+ // Gauge registration and removal
+ //
+
+ def gauge(name: String)(valueCollector: CurrentValueCollector): Gauge =
+ registerGauge(name, valueCollector)
+
+ def gauge(name: String, unitOfMeasurement: UnitOfMeasurement)(valueCollector: CurrentValueCollector): Gauge =
+ registerGauge(name, valueCollector, unitOfMeasurement = Some(unitOfMeasurement))
+
+ def gauge(name: String, dynamicRange: DynamicRange)(valueCollector: CurrentValueCollector): Gauge =
+ registerGauge(name, valueCollector, dynamicRange = Some(dynamicRange))
+
+ def gauge(name: String, refreshInterval: FiniteDuration)(valueCollector: CurrentValueCollector): Gauge =
+ registerGauge(name, valueCollector, refreshInterval = Some(refreshInterval))
+
+ def gauge(name: String, dynamicRange: DynamicRange, refreshInterval: FiniteDuration)(valueCollector: CurrentValueCollector): Gauge =
+ registerGauge(name, valueCollector, dynamicRange = Some(dynamicRange), refreshInterval = Some(refreshInterval))
+
+ def gauge(name: String, unitOfMeasurement: UnitOfMeasurement, dynamicRange: DynamicRange)(valueCollector: CurrentValueCollector): Gauge =
+ registerGauge(name, valueCollector, unitOfMeasurement = Some(unitOfMeasurement), dynamicRange = Some(dynamicRange))
+
+ def gauge(name: String, tags: Map[String, String])(valueCollector: CurrentValueCollector): Gauge =
+ registerGauge(name, valueCollector, tags)
+
+ def gauge(name: String, tags: Map[String, String], unitOfMeasurement: UnitOfMeasurement)(valueCollector: CurrentValueCollector): Gauge =
+ registerGauge(name, valueCollector, tags, Some(unitOfMeasurement))
+
+ def gauge(name: String, tags: Map[String, String], dynamicRange: DynamicRange)(valueCollector: CurrentValueCollector): Gauge =
+ registerGauge(name, valueCollector, tags, dynamicRange = Some(dynamicRange))
+
+ def gauge(name: String, tags: Map[String, String], unitOfMeasurement: UnitOfMeasurement, dynamicRange: DynamicRange)(valueCollector: CurrentValueCollector): Gauge =
+ registerGauge(name, valueCollector, tags, Some(unitOfMeasurement), Some(dynamicRange))
+
+ def removeGauge(name: String): Boolean =
+ removeGauge(name, Map.empty)
+
+ def removeGauge(name: String, tags: Map[String, String]): Boolean
+
+ def registerGauge(name: String, valueCollector: CurrentValueCollector, tags: Map[String, String] = Map.empty,
+ unitOfMeasurement: Option[UnitOfMeasurement] = None, dynamicRange: Option[DynamicRange] = None,
+ refreshInterval: Option[FiniteDuration] = None): Gauge
+
+ //
+ // Counters registration and removal
+ //
+
+ def counter(name: String): Counter =
+ registerCounter(name)
+
+ def counter(name: String, unitOfMeasurement: UnitOfMeasurement): Counter =
+ registerCounter(name, unitOfMeasurement = Some(unitOfMeasurement))
+
+ def counter(name: String, tags: Map[String, String]): Counter =
+ registerCounter(name, tags)
+
+ def counter(name: String, tags: Map[String, String], unitOfMeasurement: UnitOfMeasurement): Counter =
+ registerCounter(name, tags, Some(unitOfMeasurement))
+
+ def removeCounter(name: String): Boolean =
+ removeCounter(name, Map.empty)
+
+ def removeCounter(name: String, tags: Map[String, String]): Boolean
+
+ def registerCounter(name: String, tags: Map[String, String] = Map.empty, unitOfMeasurement: Option[UnitOfMeasurement] = None,
+ dynamicRange: Option[DynamicRange] = None): Counter
+
+ //
+ // Entities registration and removal
+ //
+
+ def entity[T <: EntityRecorder](recorderFactory: EntityRecorderFactory[T], name: String): T =
+ entity(recorderFactory, Entity(name, recorderFactory.category))
+
+ def entity[T <: EntityRecorder](recorderFactory: EntityRecorderFactory[T], name: String, tags: Map[String, String]): T =
+ entity(recorderFactory, Entity(name, recorderFactory.category, tags))
+
+ def removeEntity(name: String, category: String): Boolean =
+ removeEntity(Entity(name, category, Map.empty))
+
+ def removeEntity(name: String, category: String, tags: Map[String, String]): Boolean =
+ removeEntity(Entity(name, category, tags))
+
+ def removeEntity(entity: Entity): Boolean
+
+ def entity[T <: EntityRecorder](recorderFactory: EntityRecorderFactory[T], entity: Entity): T
+
+ def find(name: String, category: String): Option[EntityRecorder] =
+ find(Entity(name, category))
+
+ def find(name: String, category: String, tags: Map[String, String]): Option[EntityRecorder] =
+ find(Entity(name, category, tags))
def find(entity: Entity): Option[EntityRecorder]
- def find(name: String, category: String): Option[EntityRecorder]
def subscribe(filter: SubscriptionFilter, subscriber: ActorRef): Unit =
- subscribe(filter, subscriber, permanently = false)
+ subscribe(filter, subscriber, permanently = true)
def subscribe(category: String, selection: String, subscriber: ActorRef, permanently: Boolean): Unit =
subscribe(SubscriptionFilter(category, selection), subscriber, permanently)
def subscribe(category: String, selection: String, subscriber: ActorRef): Unit =
- subscribe(SubscriptionFilter(category, selection), subscriber, permanently = false)
+ subscribe(SubscriptionFilter(category, selection), subscriber, permanently = true)
def subscribe(filter: SubscriptionFilter, subscriber: ActorRef, permanently: Boolean): Unit
def unsubscribe(subscriber: ActorRef): Unit
+
def buildDefaultCollectionContext: CollectionContext
+
def instrumentFactory(category: String): InstrumentFactory
}
private[kamon] class MetricsImpl(config: Config) extends Metrics {
+ import kamon.util.TriemapAtomicGetOrElseUpdate.Syntax
+
private val _trackedEntities = TrieMap.empty[Entity, EntityRecorder]
private val _subscriptions = new LazyActorRef
@@ -67,35 +242,83 @@ private[kamon] class MetricsImpl(config: Config) extends Metrics {
} getOrElse (settings.trackUnmatchedEntities)
- def register[T <: EntityRecorder](recorderFactory: EntityRecorderFactory[T], entityName: String): Option[EntityRegistration[T]] = {
- import TriemapAtomicGetOrElseUpdate.Syntax
- val entity = Entity(entityName, recorderFactory.category)
+ def registerHistogram(name: String, tags: Map[String, String], unitOfMeasurement: Option[UnitOfMeasurement],
+ dynamicRange: Option[DynamicRange]): Histogram = {
- if (shouldTrack(entity)) {
- val instrumentFactory = settings.instrumentFactories.get(recorderFactory.category).getOrElse(settings.defaultInstrumentFactory)
- val recorder = _trackedEntities.atomicGetOrElseUpdate(entity, recorderFactory.createRecorder(instrumentFactory), _.cleanup).asInstanceOf[T]
+ val histogramEntity = Entity(name, SingleInstrumentEntityRecorder.Histogram, tags)
+ val recorder = _trackedEntities.atomicGetOrElseUpdate(histogramEntity, {
+ val factory = instrumentFactory(histogramEntity.category)
+ HistogramRecorder(HistogramKey(histogramEntity.category, unitOfMeasurement.getOrElse(UnitOfMeasurement.Unknown)),
+ factory.createHistogram(name, dynamicRange))
+ }, _.cleanup)
- Some(EntityRegistration(entity, recorder))
- } else None
+ recorder.asInstanceOf[HistogramRecorder].instrument
}
- def register[T <: EntityRecorder](entity: Entity, recorder: T): EntityRegistration[T] = {
- _trackedEntities.put(entity, recorder).map { oldRecorder ⇒
- oldRecorder.cleanup
- }
+ def removeHistogram(name: String, tags: Map[String, String]): Boolean =
+ _trackedEntities.remove(Entity(name, SingleInstrumentEntityRecorder.Histogram, tags)).isDefined
- EntityRegistration(entity, recorder)
+ def registerMinMaxCounter(name: String, tags: Map[String, String], unitOfMeasurement: Option[UnitOfMeasurement], dynamicRange: Option[DynamicRange],
+ refreshInterval: Option[FiniteDuration]): MinMaxCounter = {
+
+ val minMaxCounterEntity = Entity(name, SingleInstrumentEntityRecorder.MinMaxCounter, tags)
+ val recorder = _trackedEntities.atomicGetOrElseUpdate(minMaxCounterEntity, {
+ val factory = instrumentFactory(minMaxCounterEntity.category)
+ MinMaxCounterRecorder(MinMaxCounterKey(minMaxCounterEntity.category, unitOfMeasurement.getOrElse(UnitOfMeasurement.Unknown)),
+ factory.createMinMaxCounter(name, dynamicRange, refreshInterval))
+ }, _.cleanup)
+
+ recorder.asInstanceOf[MinMaxCounterRecorder].instrument
}
- def unregister(entity: Entity): Unit =
- _trackedEntities.remove(entity).map(_.cleanup)
+ def removeMinMaxCounter(name: String, tags: Map[String, String]): Boolean =
+ _trackedEntities.remove(Entity(name, SingleInstrumentEntityRecorder.MinMaxCounter, tags)).isDefined
+
+ def registerGauge(name: String, valueCollector: CurrentValueCollector, tags: Map[String, String] = Map.empty,
+ unitOfMeasurement: Option[UnitOfMeasurement] = None, dynamicRange: Option[DynamicRange] = None,
+ refreshInterval: Option[FiniteDuration] = None): Gauge = {
+
+ val gaugeEntity = Entity(name, SingleInstrumentEntityRecorder.Gauge, tags)
+ val recorder = _trackedEntities.atomicGetOrElseUpdate(gaugeEntity, {
+ val factory = instrumentFactory(gaugeEntity.category)
+ GaugeRecorder(MinMaxCounterKey(gaugeEntity.category, unitOfMeasurement.getOrElse(UnitOfMeasurement.Unknown)),
+ factory.createGauge(name, dynamicRange, refreshInterval, valueCollector))
+ }, _.cleanup)
+
+ recorder.asInstanceOf[GaugeRecorder].instrument
+ }
+
+ def removeGauge(name: String, tags: Map[String, String]): Boolean =
+ _trackedEntities.remove(Entity(name, SingleInstrumentEntityRecorder.Gauge, tags)).isDefined
+
+ def registerCounter(name: String, tags: Map[String, String] = Map.empty, unitOfMeasurement: Option[UnitOfMeasurement] = None,
+ dynamicRange: Option[DynamicRange] = None): Counter = {
+
+ val counterEntity = Entity(name, SingleInstrumentEntityRecorder.Counter, tags)
+ val recorder = _trackedEntities.atomicGetOrElseUpdate(counterEntity, {
+ val factory = instrumentFactory(counterEntity.category)
+ CounterRecorder(CounterKey(counterEntity.category, unitOfMeasurement.getOrElse(UnitOfMeasurement.Unknown)),
+ factory.createCounter())
+ }, _.cleanup)
+
+ recorder.asInstanceOf[CounterRecorder].instrument
+ }
+
+ def removeCounter(name: String, tags: Map[String, String]): Boolean =
+ _trackedEntities.remove(Entity(name, SingleInstrumentEntityRecorder.Counter, tags)).isDefined
+
+ def entity[T <: EntityRecorder](recorderFactory: EntityRecorderFactory[T], entity: Entity): T = {
+ _trackedEntities.atomicGetOrElseUpdate(entity, {
+ recorderFactory.createRecorder(instrumentFactory(recorderFactory.category))
+ }, _.cleanup).asInstanceOf[T]
+ }
+
+ def removeEntity(entity: Entity): Boolean =
+ _trackedEntities.remove(entity).isDefined
def find(entity: Entity): Option[EntityRecorder] =
_trackedEntities.get(entity)
- def find(name: String, category: String): Option[EntityRecorder] =
- find(Entity(name, category))
-
def subscribe(filter: SubscriptionFilter, subscriber: ActorRef, permanent: Boolean): Unit =
_subscriptions.tell(Subscribe(filter, subscriber, permanent))
diff --git a/kamon-core/src/main/scala/kamon/metric/SimpleMetrics.scala b/kamon-core/src/main/scala/kamon/metric/SimpleMetrics.scala
deleted file mode 100644
index 6324c320..00000000
--- a/kamon-core/src/main/scala/kamon/metric/SimpleMetrics.scala
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * =========================================================================================
- * Copyright © 2013-2015 the kamon project <http://kamon.io/>
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
- * either express or implied. See the License for the specific language governing permissions
- * and limitations under the License.
- * =========================================================================================
- */
-
-package kamon.metric
-
-import kamon.metric.instrument.Gauge.CurrentValueCollector
-import kamon.metric.instrument.Histogram.DynamicRange
-import kamon.metric.instrument._
-
-import scala.concurrent.duration.FiniteDuration
-
-trait SimpleMetrics {
- def histogram(name: String): Histogram
- def histogram(name: String, dynamicRange: DynamicRange): Histogram
- def histogram(name: String, unitOfMeasurement: UnitOfMeasurement): Histogram
- def histogram(name: String, dynamicRange: DynamicRange, unitOfMeasurement: UnitOfMeasurement): Histogram
- def histogram(key: HistogramKey): Histogram
- def histogram(key: HistogramKey, dynamicRange: DynamicRange): Histogram
- def removeHistogram(name: String): Unit
- def removeHistogram(key: HistogramKey): Unit
-
- def minMaxCounter(name: String): MinMaxCounter
- def minMaxCounter(name: String, dynamicRange: DynamicRange): MinMaxCounter
- def minMaxCounter(name: String, refreshInterval: FiniteDuration): MinMaxCounter
- def minMaxCounter(name: String, unitOfMeasurement: UnitOfMeasurement): MinMaxCounter
- def minMaxCounter(name: String, dynamicRange: DynamicRange, refreshInterval: FiniteDuration): MinMaxCounter
- def minMaxCounter(name: String, dynamicRange: DynamicRange, unitOfMeasurement: UnitOfMeasurement): MinMaxCounter
- def minMaxCounter(name: String, refreshInterval: FiniteDuration, unitOfMeasurement: UnitOfMeasurement): MinMaxCounter
- def minMaxCounter(name: String, dynamicRange: DynamicRange, refreshInterval: FiniteDuration, unitOfMeasurement: UnitOfMeasurement): MinMaxCounter
- def minMaxCounter(key: MinMaxCounterKey): MinMaxCounter
- def minMaxCounter(key: MinMaxCounterKey, dynamicRange: DynamicRange): MinMaxCounter
- def minMaxCounter(key: MinMaxCounterKey, refreshInterval: FiniteDuration): MinMaxCounter
- def minMaxCounter(key: MinMaxCounterKey, dynamicRange: DynamicRange, refreshInterval: FiniteDuration): MinMaxCounter
- def removeMinMaxCounter(name: String): Unit
- def removeMinMaxCounter(key: MinMaxCounterKey): Unit
-
- def gauge(name: String, valueCollector: CurrentValueCollector): Gauge
- def gauge(name: String, dynamicRange: DynamicRange, valueCollector: CurrentValueCollector): Gauge
- def gauge(name: String, refreshInterval: FiniteDuration, valueCollector: CurrentValueCollector): Gauge
- def gauge(name: String, unitOfMeasurement: UnitOfMeasurement, valueCollector: CurrentValueCollector): Gauge
- def gauge(name: String, dynamicRange: DynamicRange, refreshInterval: FiniteDuration, valueCollector: CurrentValueCollector): Gauge
- def gauge(name: String, dynamicRange: DynamicRange, unitOfMeasurement: UnitOfMeasurement, valueCollector: CurrentValueCollector): Gauge
- def gauge(name: String, refreshInterval: FiniteDuration, unitOfMeasurement: UnitOfMeasurement, valueCollector: CurrentValueCollector): Gauge
- def gauge(name: String, dynamicRange: DynamicRange, refreshInterval: FiniteDuration, unitOfMeasurement: UnitOfMeasurement, valueCollector: CurrentValueCollector): Gauge
- def gauge(key: GaugeKey, valueCollector: CurrentValueCollector): Gauge
- def gauge(key: GaugeKey, dynamicRange: DynamicRange, valueCollector: CurrentValueCollector): Gauge
- def gauge(key: GaugeKey, refreshInterval: FiniteDuration, valueCollector: CurrentValueCollector): Gauge
- def gauge(key: GaugeKey, dynamicRange: DynamicRange, refreshInterval: FiniteDuration, valueCollector: CurrentValueCollector): Gauge
- def removeGauge(name: String): Unit
- def removeGauge(key: GaugeKey): Unit
-
- def counter(name: String): Counter
- def counter(key: CounterKey): Counter
- def removeCounter(name: String): Unit
- def removeCounter(key: CounterKey): Unit
-
-}
-
-private[kamon] class SimpleMetricsImpl(instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) with SimpleMetrics {
- override def histogram(name: String): Histogram =
- super.histogram(name)
-
- override def histogram(name: String, dynamicRange: DynamicRange): Histogram =
- super.histogram(name, dynamicRange)
-
- override def histogram(name: String, unitOfMeasurement: UnitOfMeasurement): Histogram =
- super.histogram(name, unitOfMeasurement)
-
- override def histogram(name: String, dynamicRange: DynamicRange, unitOfMeasurement: UnitOfMeasurement): Histogram =
- super.histogram(name, dynamicRange, unitOfMeasurement)
-
- override def histogram(key: HistogramKey): Histogram =
- super.histogram(key)
-
- override def histogram(key: HistogramKey, dynamicRange: DynamicRange): Histogram =
- super.histogram(key, dynamicRange)
-
- override def removeHistogram(name: String): Unit =
- super.removeHistogram(name)
-
- override def removeHistogram(key: HistogramKey): Unit =
- super.removeHistogram(key)
-
- override def minMaxCounter(name: String): MinMaxCounter =
- super.minMaxCounter(name)
-
- override def minMaxCounter(name: String, dynamicRange: DynamicRange): MinMaxCounter =
- super.minMaxCounter(name, dynamicRange)
-
- override def minMaxCounter(name: String, refreshInterval: FiniteDuration): MinMaxCounter =
- super.minMaxCounter(name, refreshInterval)
-
- override def minMaxCounter(name: String, unitOfMeasurement: UnitOfMeasurement): MinMaxCounter =
- super.minMaxCounter(name, unitOfMeasurement)
-
- override def minMaxCounter(name: String, dynamicRange: DynamicRange, refreshInterval: FiniteDuration): MinMaxCounter =
- super.minMaxCounter(name, dynamicRange, refreshInterval)
-
- override def minMaxCounter(name: String, dynamicRange: DynamicRange, unitOfMeasurement: UnitOfMeasurement): MinMaxCounter =
- super.minMaxCounter(name, dynamicRange, unitOfMeasurement)
-
- override def minMaxCounter(name: String, refreshInterval: FiniteDuration, unitOfMeasurement: UnitOfMeasurement): MinMaxCounter =
- super.minMaxCounter(name, refreshInterval, unitOfMeasurement)
-
- override def minMaxCounter(name: String, dynamicRange: DynamicRange, refreshInterval: FiniteDuration, unitOfMeasurement: UnitOfMeasurement): MinMaxCounter =
- super.minMaxCounter(name, dynamicRange, refreshInterval, unitOfMeasurement)
-
- override def minMaxCounter(key: MinMaxCounterKey): MinMaxCounter =
- super.minMaxCounter(key)
-
- override def minMaxCounter(key: MinMaxCounterKey, dynamicRange: DynamicRange): MinMaxCounter =
- super.minMaxCounter(key, dynamicRange)
-
- override def minMaxCounter(key: MinMaxCounterKey, refreshInterval: FiniteDuration): MinMaxCounter =
- super.minMaxCounter(key, refreshInterval)
-
- override def minMaxCounter(key: MinMaxCounterKey, dynamicRange: DynamicRange, refreshInterval: FiniteDuration): MinMaxCounter =
- super.minMaxCounter(key, dynamicRange, refreshInterval)
-
- override def removeMinMaxCounter(name: String): Unit =
- super.removeMinMaxCounter(name)
-
- override def removeMinMaxCounter(key: MinMaxCounterKey): Unit =
- super.removeMinMaxCounter(key)
-
- override def gauge(name: String, valueCollector: CurrentValueCollector): Gauge =
- super.gauge(name, valueCollector)
-
- override def gauge(name: String, dynamicRange: DynamicRange, valueCollector: CurrentValueCollector): Gauge =
- super.gauge(name, dynamicRange, valueCollector)
-
- override def gauge(name: String, refreshInterval: FiniteDuration, valueCollector: CurrentValueCollector): Gauge =
- super.gauge(name, refreshInterval, valueCollector)
-
- override def gauge(name: String, unitOfMeasurement: UnitOfMeasurement, valueCollector: CurrentValueCollector): Gauge =
- super.gauge(name, unitOfMeasurement, valueCollector)
-
- override def gauge(name: String, dynamicRange: DynamicRange, refreshInterval: FiniteDuration, valueCollector: CurrentValueCollector): Gauge =
- super.gauge(name, dynamicRange, refreshInterval, valueCollector)
-
- override def gauge(name: String, dynamicRange: DynamicRange, unitOfMeasurement: UnitOfMeasurement, valueCollector: CurrentValueCollector): Gauge =
- super.gauge(name, dynamicRange, unitOfMeasurement, valueCollector)
-
- override def gauge(name: String, refreshInterval: FiniteDuration, unitOfMeasurement: UnitOfMeasurement, valueCollector: CurrentValueCollector): Gauge =
- super.gauge(name, refreshInterval, unitOfMeasurement, valueCollector)
-
- override def gauge(name: String, dynamicRange: DynamicRange, refreshInterval: FiniteDuration, unitOfMeasurement: UnitOfMeasurement, valueCollector: CurrentValueCollector): Gauge =
- super.gauge(name, dynamicRange, refreshInterval, unitOfMeasurement, valueCollector)
-
- override def gauge(key: GaugeKey, valueCollector: CurrentValueCollector): Gauge =
- super.gauge(key, valueCollector)
-
- override def gauge(key: GaugeKey, dynamicRange: DynamicRange, valueCollector: CurrentValueCollector): Gauge =
- super.gauge(key, dynamicRange, valueCollector)
-
- override def gauge(key: GaugeKey, refreshInterval: FiniteDuration, valueCollector: CurrentValueCollector): Gauge =
- super.gauge(key, refreshInterval, valueCollector)
-
- override def gauge(key: GaugeKey, dynamicRange: DynamicRange, refreshInterval: FiniteDuration, valueCollector: CurrentValueCollector): Gauge =
- super.gauge(key, dynamicRange, refreshInterval, valueCollector)
-
- override def removeGauge(name: String): Unit =
- super.removeGauge(name)
-
- override def removeGauge(key: GaugeKey): Unit =
- super.removeGauge(key)
-
- override def counter(name: String): Counter =
- super.counter(name)
-
- override def counter(key: CounterKey): Counter =
- super.counter(key)
-
- override def removeCounter(name: String): Unit =
- super.removeCounter(name)
-
- override def removeCounter(key: CounterKey): Unit =
- super.removeCounter(key)
-}
-
-private[kamon] object SimpleMetricsImpl {
- val SimpleMetricsEntity = Entity("simple-metric", "simple-metric")
-
- def apply(metricsExtension: Metrics): SimpleMetricsImpl = {
- val instrumentFactory = metricsExtension.instrumentFactory(SimpleMetricsEntity.category)
- val simpleMetricsExtension = new SimpleMetricsImpl(instrumentFactory)
-
- metricsExtension.register(SimpleMetricsEntity, simpleMetricsExtension).recorder
- }
-
-} \ No newline at end of file
diff --git a/kamon-core/src/main/scala/kamon/metric/TraceMetrics.scala b/kamon-core/src/main/scala/kamon/metric/TraceMetrics.scala
index 3da9c1d4..eb4f327a 100644
--- a/kamon-core/src/main/scala/kamon/metric/TraceMetrics.scala
+++ b/kamon-core/src/main/scala/kamon/metric/TraceMetrics.scala
@@ -16,29 +16,36 @@
package kamon.metric
-import kamon.metric.instrument.{ Time, InstrumentFactory, Histogram }
+import kamon.metric.instrument.{ Time, InstrumentFactory }
class TraceMetrics(instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) {
- import TraceMetrics.segmentKey
/**
* Records blah blah
*/
- val ElapsedTime = histogram("elapsed-time", unitOfMeasurement = Time.Nanoseconds)
-
- /**
- * Records Blah Blah.
- *
- */
- def segment(name: String, category: String, library: String): Histogram =
- histogram(segmentKey(name, category, library))
-
+ val elapsedTime = histogram("elapsed-time", unitOfMeasurement = Time.Nanoseconds)
}
object TraceMetrics extends EntityRecorderFactory[TraceMetrics] {
def category: String = "trace"
def createRecorder(instrumentFactory: InstrumentFactory): TraceMetrics = new TraceMetrics(instrumentFactory)
- def segmentKey(name: String, category: String, library: String): HistogramKey =
- HistogramKey(name, Time.Nanoseconds, Map("category" -> category, "library" -> library))
+ // Java API.
+ def factory: EntityRecorderFactory[TraceMetrics] = this
+}
+
+class SegmentMetrics(instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) {
+
+ /**
+ * Records blah blah
+ */
+ val elapsedTime = histogram("elapsed-time", unitOfMeasurement = Time.Nanoseconds)
+}
+
+object SegmentMetrics extends EntityRecorderFactory[SegmentMetrics] {
+ def category: String = "trace-segment"
+ def createRecorder(instrumentFactory: InstrumentFactory): SegmentMetrics = new SegmentMetrics(instrumentFactory)
+
+ // Java API.
+ def factory: EntityRecorderFactory[SegmentMetrics] = this
} \ No newline at end of file
diff --git a/kamon-core/src/main/scala/kamon/metric/instrument/Gauge.scala b/kamon-core/src/main/scala/kamon/metric/instrument/Gauge.scala
index 80214510..61b53df2 100644
--- a/kamon-core/src/main/scala/kamon/metric/instrument/Gauge.scala
+++ b/kamon-core/src/main/scala/kamon/metric/instrument/Gauge.scala
@@ -55,6 +55,10 @@ object Gauge {
implicit def functionZeroAsCurrentValueCollector(f: () ⇒ Long): CurrentValueCollector = new CurrentValueCollector {
def currentValue: Long = f.apply()
}
+
+ implicit def callByNameLongAsCurrentValueCollector(f: ⇒ Long): CurrentValueCollector = new CurrentValueCollector {
+ def currentValue: Long = f
+ }
}
/**
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 59b4b443..089dbeec 100644
--- a/kamon-core/src/main/scala/kamon/metric/instrument/Instrument.scala
+++ b/kamon-core/src/main/scala/kamon/metric/instrument/Instrument.scala
@@ -33,14 +33,6 @@ trait InstrumentSnapshot {
def merge(that: InstrumentSnapshot, context: CollectionContext): InstrumentSnapshot
}
-class InstrumentType private[kamon] (val id: Int) extends AnyVal
-object InstrumentTypes {
- val Histogram = new InstrumentType(1)
- val MinMaxCounter = new InstrumentType(2)
- val Gauge = new InstrumentType(3)
- val Counter = new InstrumentType(4)
-}
-
trait CollectionContext {
def buffer: LongBuffer
}
@@ -51,3 +43,11 @@ object CollectionContext {
}
}
+sealed trait InstrumentType
+
+object InstrumentTypes {
+ case object Histogram extends InstrumentType
+ case object MinMaxCounter extends InstrumentType
+ case object Gauge extends InstrumentType
+ case object Counter extends InstrumentType
+}
diff --git a/kamon-core/src/main/scala/kamon/metric/instrument/RefreshScheduler.scala b/kamon-core/src/main/scala/kamon/metric/instrument/RefreshScheduler.scala
index adb08713..4809ac0d 100644
--- a/kamon-core/src/main/scala/kamon/metric/instrument/RefreshScheduler.scala
+++ b/kamon-core/src/main/scala/kamon/metric/instrument/RefreshScheduler.scala
@@ -1,3 +1,19 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
package kamon.metric.instrument
import akka.actor.{ Scheduler, Cancellable }
diff --git a/kamon-core/src/main/scala/kamon/metric/instrument/UnitOfMeasurement.scala b/kamon-core/src/main/scala/kamon/metric/instrument/UnitOfMeasurement.scala
index f2a061d1..c5a1b81a 100644
--- a/kamon-core/src/main/scala/kamon/metric/instrument/UnitOfMeasurement.scala
+++ b/kamon-core/src/main/scala/kamon/metric/instrument/UnitOfMeasurement.scala
@@ -16,17 +16,20 @@
package kamon.metric.instrument
+/**
+ * A UnitOfMeasurement implementation describes the magnitude of a quantity being measured, such as Time and computer
+ * Memory space. Kamon uses UnitOfMeasurement implementations just as a informative companion to metrics inside entity
+ * recorders and might be used to scale certain kinds of measurements in metric backends.
+ */
trait UnitOfMeasurement {
def name: String
def label: String
- def factor: Double
}
object UnitOfMeasurement {
case object Unknown extends UnitOfMeasurement {
val name = "unknown"
val label = "unknown"
- val factor = 1D
}
def isUnknown(uom: UnitOfMeasurement): Boolean =
@@ -35,19 +38,18 @@ object UnitOfMeasurement {
def isTime(uom: UnitOfMeasurement): Boolean =
uom.isInstanceOf[Time]
+ def isMemory(uom: UnitOfMeasurement): Boolean =
+ uom.isInstanceOf[Memory]
+
}
+/**
+ * UnitOfMeasurement representing time.
+ */
case class Time(factor: Double, label: String) extends UnitOfMeasurement {
val name = "time"
- /**
- * Scale a value from this scale factor to a different scale factor.
- *
- * @param toUnit Time unit of the expected result.
- * @param value Value to scale.
- * @return Equivalent of value on the target time unit.
- */
- def scale(toUnit: Time)(value: Long): Double =
+ def scale(toUnit: Time)(value: Double): Double =
(value * factor) / toUnit.factor
}
@@ -58,8 +60,14 @@ object Time {
val Seconds = Time(1, "s")
}
+/**
+ * UnitOfMeasurement representing computer memory space.
+ */
case class Memory(factor: Double, label: String) extends UnitOfMeasurement {
val name = "bytes"
+
+ def scale(toUnit: Memory)(value: Double): Double =
+ (value * factor) / toUnit.factor
}
object Memory {
diff --git a/kamon-core/src/main/scala/kamon/supervisor/ModuleSupervisorExtension.scala b/kamon-core/src/main/scala/kamon/supervisor/ModuleSupervisorExtension.scala
deleted file mode 100644
index ddce63fb..00000000
--- a/kamon-core/src/main/scala/kamon/supervisor/ModuleSupervisorExtension.scala
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * =========================================================================================
- * Copyright © 2013-2015 the kamon project <http://kamon.io/>
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
- * either express or implied. See the License for the specific language governing permissions
- * and limitations under the License.
- * =========================================================================================
- */
-
-package kamon.supervisor
-
-import akka.actor
-import akka.actor._
-import kamon.Kamon
-import kamon.supervisor.KamonSupervisor.CreateModule
-
-import scala.concurrent.{ Promise, Future }
-import scala.util.Success
-
-object ModuleSupervisor extends ExtensionId[ModuleSupervisorExtension] with ExtensionIdProvider {
- def lookup(): ExtensionId[_ <: actor.Extension] = ModuleSupervisor
- def createExtension(system: ExtendedActorSystem): ModuleSupervisorExtension = new ModuleSupervisorExtensionImpl(system)
-}
-
-trait ModuleSupervisorExtension extends actor.Extension {
- def createModule(name: String, props: Props): Future[ActorRef]
-}
-
-class ModuleSupervisorExtensionImpl(system: ExtendedActorSystem) extends ModuleSupervisorExtension {
- import system.dispatcher
-
- private val _settings = ModuleSupervisorSettings(system)
- private val _supervisor = system.actorOf(KamonSupervisor.props(_settings, system.dynamicAccess), "kamon")
-
- def createModule(name: String, props: Props): Future[ActorRef] = Future {} flatMap { _: Unit ⇒
- val modulePromise = Promise[ActorRef]()
- _supervisor ! CreateModule(name, props, modulePromise)
- modulePromise.future
- }
-}
-
-class KamonSupervisor(settings: ModuleSupervisorSettings, dynamicAccess: DynamicAccess) extends Actor with ActorLogging {
-
- init()
-
- def receive = {
- case CreateModule(name, props, childPromise) ⇒ createChildModule(name, props, childPromise)
- }
-
- def createChildModule(name: String, props: Props, childPromise: Promise[ActorRef]): Unit =
- context.child(name).map { alreadyAvailableModule ⇒
- log.warning("Received a request to create module [{}] but the module is already available, returning the existent instance.")
- childPromise.complete(Success(alreadyAvailableModule))
-
- } getOrElse (childPromise.complete(Success(context.actorOf(props, name))))
-
- def init(): Unit = {
- if (settings.modulesRequiringAspectJ.nonEmpty && !isAspectJPresent && settings.showAspectJMissingWarning)
- logAspectJWeaverMissing(settings.modulesRequiringAspectJ)
-
- // Force initialization of all modules marked with auto-start.
- settings.availableModules.filter(_.autoStart).foreach { module ⇒
- if (module.extensionClass == "none")
- log.debug("Ignoring auto start of the [{}] module with no extension class.")
- else
- dynamicAccess.getObjectFor[ExtensionId[Kamon.Extension]](module.extensionClass).map { moduleID ⇒
- moduleID.get(context.system)
- log.debug("Auto starting the [{}] module.", module.name)
-
- } recover {
- case th: Throwable ⇒ log.error(th, "Failed to auto start the [{}] module.", module.name)
- }
-
- }
- }
-
- // When AspectJ is present the kamon.supervisor.AspectJPresent aspect will make this return true.
- def isAspectJPresent: Boolean = false
-
- def logAspectJWeaverMissing(modulesRequiringAspectJ: List[AvailableModuleInfo]): Unit = {
- val moduleNames = modulesRequiringAspectJ.map(_.name).mkString(", ")
- val weaverMissingMessage =
- """
- |
- | ___ _ ___ _ _ ___ ___ _ _
- | / _ \ | | |_ | | | | | | \/ |(_) (_)
- |/ /_\ \ ___ _ __ ___ ___ | |_ | | | | | | ___ __ _ __ __ ___ _ __ | . . | _ ___ ___ _ _ __ __ _
- || _ |/ __|| '_ \ / _ \ / __|| __| | | | |/\| | / _ \ / _` |\ \ / // _ \| '__| | |\/| || |/ __|/ __|| || '_ \ / _` |
- || | | |\__ \| |_) || __/| (__ | |_ /\__/ / \ /\ /| __/| (_| | \ V /| __/| | | | | || |\__ \\__ \| || | | || (_| |
- |\_| |_/|___/| .__/ \___| \___| \__|\____/ \/ \/ \___| \__,_| \_/ \___||_| \_| |_/|_||___/|___/|_||_| |_| \__, |
- | | | __/ |
- | |_| |___/
- |
- | It seems like your application was not started with the -javaagent:/path-to-aspectj-weaver.jar option but Kamon detected
- | the following modules which require AspecJ to work properly:
- |
- """.stripMargin + moduleNames +
- """
- |
- | If you need help on setting up the aspectj weaver go to http://kamon.io/introduction/get-started/ for more info. On the
- | other hand, if you are sure that you do not need or do not want to use the weaver then you can disable this error message
- | by changing the kamon.show-aspectj-missing-warning setting in your configuration file.
- |
- """.stripMargin
-
- log.error(weaverMissingMessage)
- }
-
-}
-
-object KamonSupervisor {
- case class CreateModule(name: String, props: Props, childPromise: Promise[ActorRef])
-
- def props(settings: ModuleSupervisorSettings, dynamicAccess: DynamicAccess): Props =
- Props(new KamonSupervisor(settings, dynamicAccess))
-
-}
-
diff --git a/kamon-core/src/main/scala/kamon/supervisor/ModuleSupervisorSettings.scala b/kamon-core/src/main/scala/kamon/supervisor/ModuleSupervisorSettings.scala
deleted file mode 100644
index c04157aa..00000000
--- a/kamon-core/src/main/scala/kamon/supervisor/ModuleSupervisorSettings.scala
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * =========================================================================================
- * Copyright © 2013-2015 the kamon project <http://kamon.io/>
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
- * either express or implied. See the License for the specific language governing permissions
- * and limitations under the License.
- * =========================================================================================
- */
-
-package kamon.supervisor
-
-import akka.actor.ActorSystem
-
-case class AvailableModuleInfo(name: String, extensionClass: String, requiresAspectJ: Boolean, autoStart: Boolean)
-case class ModuleSupervisorSettings(showAspectJMissingWarning: Boolean, availableModules: List[AvailableModuleInfo]) {
- val modulesRequiringAspectJ = availableModules.filter(_.requiresAspectJ)
-}
-
-object ModuleSupervisorSettings {
-
- def apply(system: ActorSystem): ModuleSupervisorSettings = {
- import kamon.util.ConfigTools.Syntax
-
- val config = system.settings.config.getConfig("kamon.modules")
- val showAspectJMissingWarning = system.settings.config.getBoolean("kamon.show-aspectj-missing-warning")
-
- val modules = config.firstLevelKeys
- val availableModules = modules.map { moduleName ⇒
- val moduleConfig = config.getConfig(moduleName)
-
- AvailableModuleInfo(
- moduleName,
- moduleConfig.getString("extension-id"),
- moduleConfig.getBoolean("requires-aspectj"),
- moduleConfig.getBoolean("auto-start"))
-
- } toList
-
- ModuleSupervisorSettings(showAspectJMissingWarning, availableModules)
- }
-
-}
diff --git a/kamon-core/src/main/scala/kamon/trace/MetricsOnlyContext.scala b/kamon-core/src/main/scala/kamon/trace/MetricsOnlyContext.scala
index eb4bad6f..0f09b4be 100644
--- a/kamon-core/src/main/scala/kamon/trace/MetricsOnlyContext.scala
+++ b/kamon-core/src/main/scala/kamon/trace/MetricsOnlyContext.scala
@@ -19,13 +19,14 @@ package kamon.trace
import java.util.concurrent.ConcurrentLinkedQueue
import akka.event.LoggingAdapter
-import kamon.metric.{ Metrics, TraceMetrics }
+import kamon.Kamon
+import kamon.metric.{ SegmentMetrics, Metrics, 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: Metrics)
+ val startTimestamp: RelativeNanoTimestamp, log: LoggingAdapter)
extends TraceContext {
@volatile private var _name = traceName
@@ -51,20 +52,23 @@ private[kamon] class MetricsOnlyContext(traceName: String, val token: String, iz
val traceElapsedTime = NanoInterval.since(startTimestamp)
_elapsedTime = traceElapsedTime
- metricsExtension.register(TraceMetrics, name).map { registration ⇒
- registration.recorder.ElapsedTime.record(traceElapsedTime.nanos)
- drainFinishedSegments(registration.recorder)
- }
+ Kamon.metrics.entity(TraceMetrics, name).elapsedTime.record(traceElapsedTime.nanos)
+ drainFinishedSegments()
}
def startSegment(segmentName: String, category: String, library: String): Segment =
new MetricsOnlySegment(segmentName, category, library)
- @tailrec private def drainFinishedSegments(recorder: TraceMetrics): Unit = {
+ @tailrec private def drainFinishedSegments(): Unit = {
val segment = _finishedSegments.poll()
if (segment != null) {
- recorder.segment(segment.name, segment.category, segment.library).record(segment.duration.nanos)
- drainFinishedSegments(recorder)
+ val segmentTags = Map(
+ "trace" -> name,
+ "category" -> segment.category,
+ "library" -> segment.library)
+
+ Kamon.metrics.entity(SegmentMetrics, segment.name, segmentTags).elapsedTime.record(segment.duration.nanos)
+ drainFinishedSegments()
}
}
@@ -72,9 +76,7 @@ private[kamon] class MetricsOnlyContext(traceName: String, val token: String, iz
_finishedSegments.add(SegmentLatencyData(segmentName, category, library, duration))
if (isClosed) {
- metricsExtension.register(TraceMetrics, name).map { registration ⇒
- drainFinishedSegments(registration.recorder)
- }
+ drainFinishedSegments()
}
}
diff --git a/kamon-core/src/main/scala/kamon/trace/Tracer.scala b/kamon-core/src/main/scala/kamon/trace/Tracer.scala
index cf83b36b..472c1d65 100644
--- a/kamon-core/src/main/scala/kamon/trace/Tracer.scala
+++ b/kamon-core/src/main/scala/kamon/trace/Tracer.scala
@@ -80,7 +80,7 @@ private[kamon] class TracerImpl(metricsExtension: Metrics, config: Config) exten
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)
+ def newMetricsOnlyContext = new MetricsOnlyContext(traceName, token, isOpen, _settings.levelOfDetail, startTimestamp, null)
if (_settings.levelOfDetail == LevelOfDetail.MetricsOnly || !isLocal)
newMetricsOnlyContext
@@ -88,7 +88,7 @@ private[kamon] class TracerImpl(metricsExtension: Metrics, config: Config) exten
if (!_settings.sampler.shouldTrace)
newMetricsOnlyContext
else
- new TracingContext(traceName, token, true, _settings.levelOfDetail, isLocal, startTimestamp, null, metricsExtension, this, dispatchTracingContext)
+ new TracingContext(traceName, token, true, _settings.levelOfDetail, isLocal, startTimestamp, null, dispatchTracingContext)
}
}
diff --git a/kamon-core/src/main/scala/kamon/trace/TracingContext.scala b/kamon-core/src/main/scala/kamon/trace/TracingContext.scala
index d34526f7..9708d25f 100644
--- a/kamon-core/src/main/scala/kamon/trace/TracingContext.scala
+++ b/kamon-core/src/main/scala/kamon/trace/TracingContext.scala
@@ -26,9 +26,8 @@ import kamon.metric.Metrics
import scala.collection.concurrent.TrieMap
private[trace] class TracingContext(traceName: String, token: String, izOpen: Boolean, levelOfDetail: LevelOfDetail,
- isLocal: Boolean, startTimeztamp: RelativeNanoTimestamp, log: LoggingAdapter, metricsExtension: Metrics,
- traceExtension: TracerImpl, traceInfoSink: TracingContext ⇒ Unit)
- extends MetricsOnlyContext(traceName, token, izOpen, levelOfDetail, startTimeztamp, log, metricsExtension) {
+ isLocal: Boolean, startTimeztamp: RelativeNanoTimestamp, log: LoggingAdapter, traceInfoSink: TracingContext ⇒ Unit)
+ extends MetricsOnlyContext(traceName, token, izOpen, levelOfDetail, startTimeztamp, log) {
private val _openSegments = new AtomicInteger(0)
private val _startTimestamp = NanoTimestamp.now
diff --git a/kamon-core/src/main/scala/kamon/supervisor/AspectJPresent.scala b/kamon-core/src/main/scala/kamon/util/FunctionalInterfaces.scala
index 0df9539f..8dab6519 100644
--- a/kamon-core/src/main/scala/kamon/supervisor/AspectJPresent.scala
+++ b/kamon-core/src/main/scala/kamon/util/FunctionalInterfaces.scala
@@ -14,18 +14,12 @@
* =========================================================================================
*/
-package kamon.supervisor
-
-import org.aspectj.lang.ProceedingJoinPoint
-import org.aspectj.lang.annotation.{ Around, Aspect, Pointcut }
-
-@Aspect
-class AspectJPresent {
-
- @Pointcut("execution(* kamon.supervisor.KamonSupervisor.isAspectJPresent())")
- def isAspectJPresentAtModuleSupervisor(): Unit = {}
-
- @Around("isAspectJPresentAtModuleSupervisor()")
- def aroundIsAspectJPresentAtModuleSupervisor(pjp: ProceedingJoinPoint): Boolean = true
+package kamon.util
+trait Supplier[T] {
+ def get: T
}
+
+trait Function[T, R] {
+ def apply(t: T): R
+} \ No newline at end of file
diff --git a/kamon-core/src/main/scala/kamon/util/JavaTags.scala b/kamon-core/src/main/scala/kamon/util/JavaTags.scala
new file mode 100644
index 00000000..90bece5c
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/util/JavaTags.scala
@@ -0,0 +1,14 @@
+package kamon.util
+
+object JavaTags {
+
+ /**
+ * Helper method to transform Java maps into Scala maps. Typically this will be used as a static import from
+ * Java code that wants to use tags, as tags are defined as scala.collection.mutable.Map[String, String] and
+ * creating them directly from Java is quite verbose.
+ */
+ def tagsFromMap(tags: java.util.Map[String, String]): Map[String, String] = {
+ import scala.collection.JavaConversions._
+ tags.toMap
+ }
+}
diff --git a/kamon-core/src/main/scala/kamon/util/Latency.scala b/kamon-core/src/main/scala/kamon/util/Latency.scala
new file mode 100644
index 00000000..52e044f8
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/util/Latency.scala
@@ -0,0 +1,29 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.util
+
+import kamon.metric.instrument.Histogram
+
+object Latency {
+ def measure[A](histogram: Histogram)(thunk: ⇒ A): A = {
+ val start = RelativeNanoTimestamp.now
+ try thunk finally {
+ val latency = NanoInterval.since(start).nanos
+ histogram.record(latency)
+ }
+ }
+}
diff --git a/kamon-core/src/main/scala/kamon/util/LazyActorRef.scala b/kamon-core/src/main/scala/kamon/util/LazyActorRef.scala
index 855bf1fc..e2fb747a 100644
--- a/kamon-core/src/main/scala/kamon/util/LazyActorRef.scala
+++ b/kamon-core/src/main/scala/kamon/util/LazyActorRef.scala
@@ -1,3 +1,19 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
package kamon.util
import java.util
diff --git a/kamon-core/src/main/scala/kamon/util/MapMerge.scala b/kamon-core/src/main/scala/kamon/util/MapMerge.scala
index 64b4f7ae..8573358b 100644
--- a/kamon-core/src/main/scala/kamon/util/MapMerge.scala
+++ b/kamon-core/src/main/scala/kamon/util/MapMerge.scala
@@ -19,7 +19,7 @@ package kamon.util
object MapMerge {
/**
- * Merge to immutable maps with the same key and value types, using the provided valueMerge function.
+ * Merge two immutable maps with the same key and value types, using the provided valueMerge function.
*/
implicit class Syntax[K, V](val map: Map[K, V]) extends AnyVal {
def merge(that: Map[K, V], valueMerge: (V, V) ⇒ V): Map[K, V] = {
diff --git a/kamon-core/src/main/scala/kamon/util/SameThreadExecutionContext.scala b/kamon-core/src/main/scala/kamon/util/SameThreadExecutionContext.scala
new file mode 100644
index 00000000..2aae526f
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/util/SameThreadExecutionContext.scala
@@ -0,0 +1,30 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.util
+
+import scala.concurrent.ExecutionContext
+import org.slf4j.LoggerFactory
+
+/**
+ * For small code blocks that don't need to be run on a separate thread.
+ */
+object SameThreadExecutionContext extends ExecutionContext {
+ val logger = LoggerFactory.getLogger("SameThreadExecutionContext")
+
+ override def execute(runnable: Runnable): Unit = runnable.run
+ override def reportFailure(t: Throwable): Unit = logger.error(t.getMessage, t)
+}
diff --git a/kamon-core/src/test/scala/kamon/metric/SimpleMetricsSpec.scala b/kamon-core/src/test/scala/kamon/metric/SimpleMetricsSpec.scala
index 60ba9a0e..0180e980 100644
--- a/kamon-core/src/test/scala/kamon/metric/SimpleMetricsSpec.scala
+++ b/kamon-core/src/test/scala/kamon/metric/SimpleMetricsSpec.scala
@@ -35,93 +35,81 @@ class SimpleMetricsSpec extends BaseKamonSpec("simple-metrics-spec") {
"the SimpleMetrics extension" should {
"allow registering a fully configured Histogram and get the same Histogram if registering again" in {
- val histogramA = Kamon.simpleMetrics.histogram("histogram-with-settings", DynamicRange(1, 10000, 2))
- val histogramB = Kamon.simpleMetrics.histogram("histogram-with-settings", DynamicRange(1, 10000, 2))
+ val histogramA = Kamon.metrics.histogram("histogram-with-settings", DynamicRange(1, 10000, 2))
+ val histogramB = Kamon.metrics.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.simpleMetrics.histogram("histogram-with-settings", DynamicRange(1, 10000, 2))
- val histogramB = Kamon.simpleMetrics.histogram("histogram-with-settings", DynamicRange(1, 50000, 2))
+ val histogramA = Kamon.metrics.histogram("histogram-with-settings", DynamicRange(1, 10000, 2))
+ val histogramB = Kamon.metrics.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.simpleMetrics.histogram("histogram-with-default-configuration")
+ Kamon.metrics.histogram("histogram-with-default-configuration")
}
"allow registering a Counter and get the same Counter if registering again" in {
- val counterA = Kamon.simpleMetrics.counter("counter")
- val counterB = Kamon.simpleMetrics.counter("counter")
+ val counterA = Kamon.metrics.counter("counter")
+ val counterB = Kamon.metrics.counter("counter")
counterA shouldBe theSameInstanceAs(counterB)
}
"allow registering a fully configured MinMaxCounter and get the same MinMaxCounter if registering again" in {
- val minMaxCounterA = Kamon.simpleMetrics.minMaxCounter("min-max-counter-with-settings", DynamicRange(1, 10000, 2), 1 second)
- val minMaxCounterB = Kamon.simpleMetrics.minMaxCounter("min-max-counter-with-settings", DynamicRange(1, 10000, 2), 1 second)
+ val minMaxCounterA = Kamon.metrics.minMaxCounter("min-max-counter-with-settings", DynamicRange(1, 10000, 2), 1 second)
+ val minMaxCounterB = Kamon.metrics.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.simpleMetrics.minMaxCounter("min-max-counter-with-settings", DynamicRange(1, 10000, 2), 1 second)
- val minMaxCounterB = Kamon.simpleMetrics.minMaxCounter("min-max-counter-with-settings", DynamicRange(1, 50000, 2), 1 second)
+ val minMaxCounterA = Kamon.metrics.minMaxCounter("min-max-counter-with-settings", DynamicRange(1, 10000, 2), 1 second)
+ val minMaxCounterB = Kamon.metrics.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.simpleMetrics.minMaxCounter("min-max-counter-with-default-configuration")
+ Kamon.metrics.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.simpleMetrics.gauge("gauge-with-settings", DynamicRange(1, 10000, 2), 1 second, {
- () ⇒ 1L
- })
-
- val gaugeB = Kamon.simpleMetrics.gauge("gauge-with-settings", DynamicRange(1, 10000, 2), 1 second, {
- () ⇒ 1L
- })
+ val gaugeA = Kamon.metrics.gauge("gauge-with-settings", DynamicRange(1, 10000, 2), 1 second)(1L)
+ val gaugeB = Kamon.metrics.gauge("gauge-with-settings", DynamicRange(1, 10000, 2), 1 second)(1L)
gaugeA shouldBe theSameInstanceAs(gaugeB)
}
"return the original Gauge when registering a fully configured Gauge for second time but with different settings" in {
- val gaugeA = Kamon.simpleMetrics.gauge("gauge-with-settings", DynamicRange(1, 10000, 2), 1 second, {
- () ⇒ 1L
- })
-
- val gaugeB = Kamon.simpleMetrics.gauge("gauge-with-settings", DynamicRange(1, 10000, 2), 1 second, {
- () ⇒ 1L
- })
+ val gaugeA = Kamon.metrics.gauge("gauge-with-settings", DynamicRange(1, 10000, 2), 1 second)(1L)
+ val gaugeB = Kamon.metrics.gauge("gauge-with-settings", DynamicRange(1, 10000, 2), 1 second)(1L)
gaugeA shouldBe theSameInstanceAs(gaugeB)
}
"allow registering a Gauge that takes the default configuration from the kamon.metrics.precision settings" in {
- Kamon.simpleMetrics.gauge("gauge-with-default-configuration", {
- () ⇒ 2L
- })
+ Kamon.metrics.gauge("gauge-with-default-configuration")(2L)
}
"allow un-registering user metrics" in {
- val counter = Kamon.simpleMetrics.counter("counter-for-remove")
- val histogram = Kamon.simpleMetrics.histogram("histogram-for-remove")
- val minMaxCounter = Kamon.simpleMetrics.minMaxCounter("min-max-counter-for-remove")
- val gauge = Kamon.simpleMetrics.gauge("gauge-for-remove", { () ⇒ 2L })
-
- Kamon.simpleMetrics.removeCounter("counter-for-remove")
- Kamon.simpleMetrics.removeHistogram("histogram-for-remove")
- Kamon.simpleMetrics.removeMinMaxCounter("min-max-counter-for-remove")
- Kamon.simpleMetrics.removeGauge("gauge-for-remove")
-
- counter should not be (theSameInstanceAs(Kamon.simpleMetrics.counter("counter-for-remove")))
- histogram should not be (theSameInstanceAs(Kamon.simpleMetrics.histogram("histogram-for-remove")))
- minMaxCounter should not be (theSameInstanceAs(Kamon.simpleMetrics.minMaxCounter("min-max-counter-for-remove")))
- gauge should not be (theSameInstanceAs(Kamon.simpleMetrics.gauge("gauge-for-remove", { () ⇒ 2L })))
+ val counter = Kamon.metrics.counter("counter-for-remove")
+ val histogram = Kamon.metrics.histogram("histogram-for-remove")
+ val minMaxCounter = Kamon.metrics.minMaxCounter("min-max-counter-for-remove")
+ val gauge = Kamon.metrics.gauge("gauge-for-remove")(2L)
+
+ Kamon.metrics.removeCounter("counter-for-remove")
+ Kamon.metrics.removeHistogram("histogram-for-remove")
+ Kamon.metrics.removeMinMaxCounter("min-max-counter-for-remove")
+ Kamon.metrics.removeGauge("gauge-for-remove")
+
+ counter should not be (theSameInstanceAs(Kamon.metrics.counter("counter-for-remove")))
+ histogram should not be (theSameInstanceAs(Kamon.metrics.histogram("histogram-for-remove")))
+ minMaxCounter should not be (theSameInstanceAs(Kamon.metrics.minMaxCounter("min-max-counter-for-remove")))
+ gauge should not be (theSameInstanceAs(Kamon.metrics.gauge("gauge-for-remove")(2L)))
}
}
}
diff --git a/kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala b/kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala
index 53ae5273..b8131f6e 100644
--- a/kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala
+++ b/kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala
@@ -34,12 +34,12 @@ class SubscriptionsProtocolSpec extends BaseKamonSpec("subscriptions-protocol-sp
""".stripMargin)
lazy val metricsModule = Kamon.metrics
- import metricsModule.{ register, subscribe, unsubscribe }
+ import metricsModule.{ entity, subscribe, unsubscribe }
"the Subscriptions messaging protocol" should {
"allow subscribing for a single tick" in {
val subscriber = TestProbe()
- register(TraceMetrics, "one-shot")
+ entity(TraceMetrics, "one-shot")
subscribe("trace", "one-shot", subscriber.ref, permanently = false)
flushSubscriptions()
@@ -54,7 +54,7 @@ class SubscriptionsProtocolSpec extends BaseKamonSpec("subscriptions-protocol-sp
"allow subscribing permanently to a metric" in {
val subscriber = TestProbe()
- register(TraceMetrics, "permanent")
+ entity(TraceMetrics, "permanent")
subscribe("trace", "permanent", subscriber.ref, permanently = true)
for (repetition ← 1 to 5) {
@@ -68,9 +68,9 @@ class SubscriptionsProtocolSpec extends BaseKamonSpec("subscriptions-protocol-sp
"allow subscribing to metrics matching a glob pattern" in {
val subscriber = TestProbe()
- register(TraceMetrics, "include-one")
- register(TraceMetrics, "exclude-two")
- register(TraceMetrics, "include-three")
+ entity(TraceMetrics, "include-one")
+ entity(TraceMetrics, "exclude-two")
+ entity(TraceMetrics, "include-three")
subscribe("trace", "include-*", subscriber.ref, permanently = true)
for (repetition ← 1 to 5) {
@@ -85,9 +85,9 @@ class SubscriptionsProtocolSpec extends BaseKamonSpec("subscriptions-protocol-sp
"send a single TickMetricSnapshot to each subscriber, even if subscribed multiple times" in {
val subscriber = TestProbe()
- register(TraceMetrics, "include-one")
- register(TraceMetrics, "exclude-two")
- register(TraceMetrics, "include-three")
+ entity(TraceMetrics, "include-one")
+ entity(TraceMetrics, "exclude-two")
+ entity(TraceMetrics, "include-three")
subscribe("trace", "include-one", subscriber.ref, permanently = true)
subscribe("trace", "include-three", subscriber.ref, permanently = true)
@@ -103,7 +103,7 @@ class SubscriptionsProtocolSpec extends BaseKamonSpec("subscriptions-protocol-sp
"allow un-subscribing a subscriber" in {
val subscriber = TestProbe()
- register(TraceMetrics, "one-shot")
+ entity(TraceMetrics, "one-shot")
subscribe("trace", "one-shot", subscriber.ref, permanently = true)
flushSubscriptions()
diff --git a/kamon-core/src/test/scala/kamon/metric/TickMetricSnapshotBufferSpec.scala b/kamon-core/src/test/scala/kamon/metric/TickMetricSnapshotBufferSpec.scala
index 0c9ced32..ac35cf58 100644
--- a/kamon-core/src/test/scala/kamon/metric/TickMetricSnapshotBufferSpec.scala
+++ b/kamon-core/src/test/scala/kamon/metric/TickMetricSnapshotBufferSpec.scala
@@ -88,21 +88,21 @@ class TickMetricSnapshotBufferSpec extends BaseKamonSpec("trace-metrics-spec") w
trait SnapshotFixtures {
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.entity(TraceMetrics, "buffer-spec-test-trace")
val firstEmpty = TickMetricSnapshot(new MilliTimestamp(1000), new MilliTimestamp(2000), Map.empty)
val secondEmpty = TickMetricSnapshot(new MilliTimestamp(2000), new MilliTimestamp(3000), Map.empty)
val thirdEmpty = TickMetricSnapshot(new MilliTimestamp(3000), new MilliTimestamp(4000), Map.empty)
- traceRecorder.ElapsedTime.record(10L)
- traceRecorder.ElapsedTime.record(20L)
- traceRecorder.ElapsedTime.record(30L)
+ traceRecorder.elapsedTime.record(10L)
+ traceRecorder.elapsedTime.record(20L)
+ traceRecorder.elapsedTime.record(30L)
val firstNonEmpty = TickMetricSnapshot(new MilliTimestamp(1000), new MilliTimestamp(2000), Map(
(testTraceIdentity -> traceRecorder.collect(collectionContext))))
- traceRecorder.ElapsedTime.record(10L)
- traceRecorder.ElapsedTime.record(10L)
- traceRecorder.ElapsedTime.record(300L)
+ traceRecorder.elapsedTime.record(10L)
+ traceRecorder.elapsedTime.record(10L)
+ traceRecorder.elapsedTime.record(300L)
val secondNonEmpty = TickMetricSnapshot(new MilliTimestamp(1000), new MilliTimestamp(2000), Map(
(testTraceIdentity -> traceRecorder.collect(collectionContext))))
}
diff --git a/kamon-core/src/test/scala/kamon/metric/TraceMetricsSpec.scala b/kamon-core/src/test/scala/kamon/metric/TraceMetricsSpec.scala
index 03a09b7f..efdcb79b 100644
--- a/kamon-core/src/test/scala/kamon/metric/TraceMetricsSpec.scala
+++ b/kamon-core/src/test/scala/kamon/metric/TraceMetricsSpec.scala
@@ -23,8 +23,6 @@ import kamon.trace.Tracer
import kamon.metric.instrument.Histogram
class TraceMetricsSpec extends BaseKamonSpec("trace-metrics-spec") with ImplicitSender {
- import TraceMetricsSpec.SegmentSyntax
-
override lazy val config =
ConfigFactory.parseString(
"""
@@ -60,10 +58,13 @@ class TraceMetricsSpec extends BaseKamonSpec("trace-metrics-spec") with Implicit
Tracer.currentContext.finish()
}
- val snapshot = takeSnapshotOf("trace-with-segments", "trace")
+ val snapshot = takeSnapshotOf("test-segment", "trace-segment",
+ tags = Map(
+ "trace" -> "trace-with-segments",
+ "category" -> "test-category",
+ "library" -> "test-library"))
+
snapshot.histogram("elapsed-time").get.numberOfMeasurements should be(1)
- snapshot.segments.size should be(1)
- snapshot.segment("test-segment", "test-category", "test-library").numberOfMeasurements should be(1)
}
"record the elapsed time for segments that finish after their correspondent trace has finished" in {
@@ -75,25 +76,26 @@ class TraceMetricsSpec extends BaseKamonSpec("trace-metrics-spec") with Implicit
val beforeFinishSegmentSnapshot = takeSnapshotOf("closing-segment-after-trace", "trace")
beforeFinishSegmentSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(1)
- beforeFinishSegmentSnapshot.segments.size should be(0)
+
+ intercept[NoSuchElementException] {
+ // The segment metric should not exist before we it has finished.
+
+ takeSnapshotOf("test-segment", "trace-segment",
+ tags = Map(
+ "trace" -> "closing-segment-after-trace",
+ "category" -> "test-category",
+ "library" -> "test-library"))
+ }
segment.finish()
- val afterFinishSegmentSnapshot = takeSnapshotOf("closing-segment-after-trace", "trace")
- afterFinishSegmentSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(0)
- afterFinishSegmentSnapshot.segments.size should be(1)
- afterFinishSegmentSnapshot.segment("test-segment", "test-category", "test-library").numberOfMeasurements should be(1)
- }
- }
-}
+ val afterFinishSegmentSnapshot = takeSnapshotOf("test-segment", "trace-segment",
+ tags = Map(
+ "trace" -> "closing-segment-after-trace",
+ "category" -> "test-category",
+ "library" -> "test-library"))
-object TraceMetricsSpec {
- implicit class SegmentSyntax(val entitySnapshot: EntitySnapshot) extends AnyVal {
- def segments: Map[HistogramKey, Histogram.Snapshot] = {
- entitySnapshot.histograms.filterKeys(_.metadata.contains("category"))
+ afterFinishSegmentSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(1)
}
-
- def segment(name: String, category: String, library: String): Histogram.Snapshot =
- segments(TraceMetrics.segmentKey(name, category, library))
}
}
diff --git a/kamon-core/src/test/scala/kamon/testkit/BaseKamonSpec.scala b/kamon-core/src/test/scala/kamon/testkit/BaseKamonSpec.scala
index f3d809bf..e7b18770 100644
--- a/kamon-core/src/test/scala/kamon/testkit/BaseKamonSpec.scala
+++ b/kamon-core/src/test/scala/kamon/testkit/BaseKamonSpec.scala
@@ -20,7 +20,7 @@ import akka.testkit.{ ImplicitSender, TestKitBase }
import akka.actor.ActorSystem
import com.typesafe.config.{ Config, ConfigFactory }
import kamon.Kamon
-import kamon.metric.{ SubscriptionsDispatcher, EntitySnapshot, MetricsImpl }
+import kamon.metric.{ Entity, SubscriptionsDispatcher, EntitySnapshot, MetricsImpl }
import kamon.trace.TraceContext
import kamon.util.LazyActorRef
import org.scalatest.{ BeforeAndAfterAll, Matchers, WordSpecLike }
@@ -48,6 +48,11 @@ abstract class BaseKamonSpec(actorSystemName: String) extends TestKitBase with W
recorder.collect(collectionContext)
}
+ def takeSnapshotOf(name: String, category: String, tags: Map[String, String]): EntitySnapshot = {
+ val recorder = Kamon.metrics.find(Entity(name, category, tags)).get
+ recorder.collect(collectionContext)
+ }
+
def flushSubscriptions(): Unit = {
val subscriptionsField = Kamon.metrics.getClass.getDeclaredField("_subscriptions")
subscriptionsField.setAccessible(true)
diff --git a/kamon-datadog/src/main/scala/kamon/datadog/DatadogMetricsSender.scala b/kamon-datadog/src/main/scala/kamon/datadog/DatadogMetricsSender.scala
index 80d4f098..4a73f5aa 100644
--- a/kamon-datadog/src/main/scala/kamon/datadog/DatadogMetricsSender.scala
+++ b/kamon-datadog/src/main/scala/kamon/datadog/DatadogMetricsSender.scala
@@ -23,7 +23,7 @@ import akka.util.ByteString
import kamon.metric.SubscriptionsDispatcher.TickMetricSnapshot
import java.text.{ DecimalFormatSymbols, DecimalFormat }
import kamon.metric.instrument.{ Counter, Histogram }
-import kamon.metric.{ MetricKey, Entity }
+import kamon.metric.{ SingleInstrumentEntityRecorder, MetricKey, Entity }
import java.util.Locale
class DatadogMetricsSender(remote: InetSocketAddress, maxPacketSizeInBytes: Long) extends Actor with UdpExtensionProvider {
@@ -90,11 +90,18 @@ class DatadogMetricsSender(remote: InetSocketAddress, maxPacketSizeInBytes: Long
def encodeDatadogCounter(count: Long): String = count.toString + "|c"
def buildMetricName(entity: Entity, metricKey: MetricKey): String =
- s"$appName.${entity.category}.${metricKey.name}"
+ if (SingleInstrumentEntityRecorder.AllCategories.contains(entity.category))
+ s"$appName.${entity.category}"
+ else
+ s"$appName.${entity.category}.${metricKey.name}"
def buildIdentificationTag(entity: Entity, metricKey: MetricKey): String = {
- val normalizedEntityName = entity.name.replace(": ", ":")
- s"|#${entity.category}:${normalizedEntityName}"
+ val normalizedEntityName = entity.name.replace(" ", "")
+ if (entity.tags.nonEmpty) {
+ val tagsString = entity.tags.map { case (k, v) ⇒ k + ":" + v } mkString ","
+ s"|#${entity.category}:${normalizedEntityName},$tagsString"
+ } else
+ s"|#${entity.category}:${normalizedEntityName}"
}
}
diff --git a/kamon-datadog/src/test/scala/kamon/datadog/DatadogMetricSenderSpec.scala b/kamon-datadog/src/test/scala/kamon/datadog/DatadogMetricSenderSpec.scala
index b35902f9..22ce1f8e 100644
--- a/kamon-datadog/src/test/scala/kamon/datadog/DatadogMetricSenderSpec.scala
+++ b/kamon-datadog/src/test/scala/kamon/datadog/DatadogMetricSenderSpec.scala
@@ -96,7 +96,20 @@ class DatadogMetricSenderSpec extends BaseKamonSpec("datadog-metric-sender-spec"
val udp = setup(Map(entity -> testRecorder.collect(collectionContext)))
val Udp.Send(data, _, _) = udp.expectMsgType[Udp.Send]
- data.utf8String should be("kamon.category.metric-one:10|ms|@0.5|#category:datadog\nkamon.category.counter:4|c|#category:datadog\nkamon.category.metric-two:21|ms|#category:datadog")
+ data.utf8String.split("\n") should contain allOf (
+ "kamon.category.metric-one:10|ms|@0.5|#category:datadog",
+ "kamon.category.metric-two:21|ms|#category:datadog",
+ "kamon.category.counter:4|c|#category:datadog")
+ }
+
+ "include all entity tags, if available in the metric packet" in new UdpListenerFixture {
+ val (entity, testRecorder) = buildRecorder("datadog", tags = Map("my-cool-tag" -> "some-value"))
+ testRecorder.metricTwo.record(10L, 2)
+
+ val udp = setup(Map(entity -> testRecorder.collect(collectionContext)))
+ val Udp.Send(data, _, _) = udp.expectMsgType[Udp.Send]
+
+ data.utf8String should be(s"kamon.category.metric-two:10|ms|@0.5|#category:datadog,my-cool-tag:some-value")
}
}
@@ -105,9 +118,10 @@ class DatadogMetricSenderSpec extends BaseKamonSpec("datadog-metric-sender-spec"
val localhostName = ManagementFactory.getRuntimeMXBean.getName.split('@')(1)
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
- (registration.entity, registration.recorder)
+ def buildRecorder(name: String, tags: Map[String, String] = Map.empty): (Entity, TestEntityRecorder) = {
+ val entity = Entity(name, TestEntityRecorder.category, tags)
+ val recorder = Kamon.metrics.entity(TestEntityRecorder, entity)
+ (entity, recorder)
}
def setup(metrics: Map[Entity, EntitySnapshot]): TestProbe = {
diff --git a/kamon-examples/kamon-play-example/app/Global.scala b/kamon-examples/kamon-play-example/app/Global.scala
index 535ea308..8d43fbdc 100644
--- a/kamon-examples/kamon-play-example/app/Global.scala
+++ b/kamon-examples/kamon-play-example/app/Global.scala
@@ -16,5 +16,11 @@
import filters.TraceLocalFilter
import play.api.mvc.WithFilters
+import play.api.Application
+import kamon.Kamon
-object Global extends WithFilters(TraceLocalFilter) \ No newline at end of file
+object Global extends WithFilters(TraceLocalFilter) {
+ override def onStart(app: Application) {
+ Kamon.start()
+ }
+}
diff --git a/kamon-examples/kamon-play-example/app/controllers/KamonPlayExample.scala b/kamon-examples/kamon-play-example/app/controllers/KamonPlayExample.scala
index b3a8c11f..dea2b879 100644
--- a/kamon-examples/kamon-play-example/app/controllers/KamonPlayExample.scala
+++ b/kamon-examples/kamon-play-example/app/controllers/KamonPlayExample.scala
@@ -17,7 +17,6 @@ package controllers
import filters.{TraceLocalContainer, TraceLocalKey}
import kamon.Kamon
-import kamon.metric.UserMetrics
import kamon.play.action.TraceName
import kamon.trace.TraceLocal
import play.api.Logger
@@ -56,7 +55,7 @@ import scala.concurrent._
object KamonPlayExample extends Controller {
val logger = Logger(this.getClass)
- val counter = Kamon(UserMetrics)(Akka.system()).registerCounter("my-counter")
+ val counter = Kamon.metrics.counter("my-counter")
def sayHello = Action.async {
Future {
diff --git a/kamon-examples/kamon-play-example/app/filters/TraceLocalFilter.scala b/kamon-examples/kamon-play-example/app/filters/TraceLocalFilter.scala
index c1d5b92e..8a92aa66 100644
--- a/kamon-examples/kamon-play-example/app/filters/TraceLocalFilter.scala
+++ b/kamon-examples/kamon-play-example/app/filters/TraceLocalFilter.scala
@@ -16,7 +16,7 @@
package filters
-import kamon.trace.{TraceRecorder, TraceLocal}
+import kamon.trace.TraceLocal
import play.api.Logger
import play.api.mvc.{Result, RequestHeader, Filter}
import play.api.libs.concurrent.Execution.Implicits.defaultContext
diff --git a/kamon-examples/kamon-play-example/conf/application.conf b/kamon-examples/kamon-play-example/conf/application.conf
index 7d9fba80..e1b4d770 100644
--- a/kamon-examples/kamon-play-example/conf/application.conf
+++ b/kamon-examples/kamon-play-example/conf/application.conf
@@ -5,7 +5,7 @@ akka {
kamon {
- metrics {
+ metric {
tick-interval = 1 second
}
diff --git a/kamon-examples/kamon-play-example/project/Build.scala b/kamon-examples/kamon-play-example/project/Build.scala
index 080ef4a5..1896c932 100644
--- a/kamon-examples/kamon-play-example/project/Build.scala
+++ b/kamon-examples/kamon-play-example/project/Build.scala
@@ -20,7 +20,7 @@ object ApplicationBuild extends Build {
)
val defaultSettings = Seq(
- scalaVersion := "2.10.4",
+ scalaVersion := "2.11.5",
resolvers ++= resolutionRepos,
scalacOptions := Seq(
"-encoding",
@@ -35,7 +35,7 @@ object ApplicationBuild extends Build {
"-Xlog-reflective-calls"
))
- val kamonVersion = "0.3.4"
+ val kamonVersion = "0.3.6-125bee567e0b18ea5ec10f3a1fe76409673011d9"
val dependencies = Seq(
"io.kamon" %% "kamon-core" % kamonVersion,
diff --git a/kamon-examples/kamon-play-example/project/plugins.sbt b/kamon-examples/kamon-play-example/project/plugins.sbt
index 6f7c1c8b..b4f51541 100644
--- a/kamon-examples/kamon-play-example/project/plugins.sbt
+++ b/kamon-examples/kamon-play-example/project/plugins.sbt
@@ -5,5 +5,5 @@ logLevel := Level.Warn
resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
// Use the Play sbt plugin for Play projects
-addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.4")
+addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.8")
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 aa9295db..bef20667 100644
--- a/kamon-jdbc/src/main/scala/kamon/jdbc/instrumentation/StatementInstrumentation.scala
+++ b/kamon-jdbc/src/main/scala/kamon/jdbc/instrumentation/StatementInstrumentation.scala
@@ -44,9 +44,8 @@ class StatementInstrumentation {
@Around("onExecuteStatement(sql) || onExecutePreparedStatement(sql) || onExecutePreparedCall(sql)")
def aroundExecuteStatement(pjp: ProceedingJoinPoint, sql: String): Any = {
Tracer.currentContext.collect { ctx ⇒
- val metricsExtension = Kamon.metrics
val jdbcExtension = Kamon(Jdbc)
- implicit val statementRecorder = metricsExtension.register(StatementsMetrics, "jdbc-statements").map(_.recorder)
+ implicit val statementRecorder = Kamon.metrics.entity(StatementsMetrics, "jdbc-statements")
sql.replaceAll(CommentPattern, Empty) match {
case SelectStatement(_) ⇒ withSegment(ctx, Select, jdbcExtension)(recordRead(pjp, sql, jdbcExtension))
@@ -71,22 +70,22 @@ class StatementInstrumentation {
try thunk finally segment.finish()
}
- def recordRead(pjp: ProceedingJoinPoint, sql: String, jdbcExtension: JdbcExtension)(implicit statementRecorder: Option[StatementsMetrics]): Any = {
+ def recordRead(pjp: ProceedingJoinPoint, sql: String, jdbcExtension: JdbcExtension)(implicit statementRecorder: StatementsMetrics): Any = {
withTimeSpent(pjp.proceedWithErrorHandler(sql, jdbcExtension)) { timeSpent ⇒
- statementRecorder.map(stmr ⇒ stmr.reads.record(timeSpent))
+ statementRecorder.reads.record(timeSpent)
val timeSpentInMillis = nanos.toMillis(timeSpent)
if (timeSpentInMillis >= jdbcExtension.slowQueryThreshold) {
- statementRecorder.map(stmr ⇒ stmr.slows.increment())
+ statementRecorder.slows.increment()
jdbcExtension.processSlowQuery(sql, timeSpentInMillis)
}
}
}
- def recordWrite(pjp: ProceedingJoinPoint, sql: String, jdbcExtension: JdbcExtension)(implicit statementRecorder: Option[StatementsMetrics]): Any = {
+ def recordWrite(pjp: ProceedingJoinPoint, sql: String, jdbcExtension: JdbcExtension)(implicit statementRecorder: StatementsMetrics): Any = {
withTimeSpent(pjp.proceedWithErrorHandler(sql, jdbcExtension)) { timeSpent ⇒
- statementRecorder.map(stmr ⇒ stmr.writes.record(timeSpent))
+ statementRecorder.writes.record(timeSpent)
}
}
}
@@ -107,13 +106,13 @@ object StatementInstrumentation {
val Delete = "Delete"
implicit class PimpedProceedingJoinPoint(pjp: ProceedingJoinPoint) {
- def proceedWithErrorHandler(sql: String, jdbcExtension: JdbcExtension)(implicit statementRecorder: Option[StatementsMetrics]): Any = {
+ def proceedWithErrorHandler(sql: String, jdbcExtension: JdbcExtension)(implicit statementRecorder: StatementsMetrics): Any = {
try {
pjp.proceed()
} catch {
case NonFatal(cause) ⇒
jdbcExtension.processSqlError(sql, cause)
- statementRecorder.map(stmr ⇒ stmr.errors.increment())
+ statementRecorder.errors.increment()
throw cause
}
}
diff --git a/kamon-jdbc/src/test/scala/kamon/jdbc/instrumentation/StatementInstrumentationSpec.scala b/kamon-jdbc/src/test/scala/kamon/jdbc/instrumentation/StatementInstrumentationSpec.scala
index 8ad5faa8..80107dff 100644
--- a/kamon-jdbc/src/test/scala/kamon/jdbc/instrumentation/StatementInstrumentationSpec.scala
+++ b/kamon-jdbc/src/test/scala/kamon/jdbc/instrumentation/StatementInstrumentationSpec.scala
@@ -24,8 +24,6 @@ import kamon.testkit.BaseKamonSpec
import kamon.trace.{ Tracer, SegmentCategory }
class StatementInstrumentationSpec extends BaseKamonSpec("jdbc-spec") {
- import TraceMetricsSpec.SegmentSyntax
-
override lazy val config =
ConfigFactory.parseString(
"""
@@ -76,8 +74,14 @@ class StatementInstrumentationSpec extends BaseKamonSpec("jdbc-spec") {
val traceSnapshot = takeSnapshotOf("jdbc-trace-insert", "trace")
traceSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(1)
- traceSnapshot.segments.size should be(1)
- traceSnapshot.segment("Jdbc[Insert]", SegmentCategory.Database, Jdbc.SegmentLibraryName).numberOfMeasurements should be(100)
+
+ val segmentSnapshot = takeSnapshotOf("Jdbc[Insert]", "trace-segment",
+ tags = Map(
+ "trace" -> "jdbc-trace-insert",
+ "category" -> SegmentCategory.Database,
+ "library" -> Jdbc.SegmentLibraryName))
+
+ segmentSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(100)
}
"record the execution time of SELECT operation" in {
@@ -96,8 +100,14 @@ class StatementInstrumentationSpec extends BaseKamonSpec("jdbc-spec") {
val traceSnapshot = takeSnapshotOf("jdbc-trace-select", "trace")
traceSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(1)
- traceSnapshot.segments.size should be(1)
- traceSnapshot.segment("Jdbc[Select]", SegmentCategory.Database, Jdbc.SegmentLibraryName).numberOfMeasurements should be(100)
+
+ val segmentSnapshot = takeSnapshotOf("Jdbc[Select]", "trace-segment",
+ tags = Map(
+ "trace" -> "jdbc-trace-select",
+ "category" -> SegmentCategory.Database,
+ "library" -> Jdbc.SegmentLibraryName))
+
+ segmentSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(100)
}
"record the execution time of UPDATE operation" in {
@@ -116,8 +126,14 @@ class StatementInstrumentationSpec extends BaseKamonSpec("jdbc-spec") {
val traceSnapshot = takeSnapshotOf("jdbc-trace-update", "trace")
traceSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(1)
- traceSnapshot.segments.size should be(1)
- traceSnapshot.segment("Jdbc[Update]", SegmentCategory.Database, Jdbc.SegmentLibraryName).numberOfMeasurements should be(100)
+
+ val segmentSnapshot = takeSnapshotOf("Jdbc[Update]", "trace-segment",
+ tags = Map(
+ "trace" -> "jdbc-trace-update",
+ "category" -> SegmentCategory.Database,
+ "library" -> Jdbc.SegmentLibraryName))
+
+ segmentSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(100)
}
"record the execution time of DELETE operation" in {
@@ -136,8 +152,14 @@ class StatementInstrumentationSpec extends BaseKamonSpec("jdbc-spec") {
val traceSnapshot = takeSnapshotOf("jdbc-trace-delete", "trace")
traceSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(1)
- traceSnapshot.segments.size should be(1)
- traceSnapshot.segment("Jdbc[Delete]", SegmentCategory.Database, Jdbc.SegmentLibraryName).numberOfMeasurements should be(100)
+
+ val segmentSnapshot = takeSnapshotOf("Jdbc[Delete]", "trace-segment",
+ tags = Map(
+ "trace" -> "jdbc-trace-delete",
+ "category" -> SegmentCategory.Database,
+ "library" -> Jdbc.SegmentLibraryName))
+
+ segmentSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(100)
}
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 fc1f0faf..27424e39 100644
--- a/kamon-log-reporter/src/main/scala/kamon/logreporter/LogReporter.scala
+++ b/kamon-log-reporter/src/main/scala/kamon/logreporter/LogReporter.scala
@@ -1,6 +1,6 @@
/*
* =========================================================================================
- * Copyright © 2013-2014 the kamon project <http://kamon.io/>
+ * Copyright © 2013-2015 the kamon project <http://kamon.io/>
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
@@ -36,8 +36,12 @@ class LogReporterExtension(system: ExtendedActorSystem) extends Kamon.Extension
val subscriber = system.actorOf(Props[LogReporterSubscriber], "kamon-log-reporter")
Kamon.metrics.subscribe("trace", "**", subscriber, permanently = true)
- Kamon.metrics.subscribe("actor", "**", subscriber, permanently = true)
- Kamon.metrics.subscribe("user-metrics", "**", subscriber, permanently = true)
+ Kamon.metrics.subscribe("akka-actor", "**", subscriber, permanently = true)
+ Kamon.metrics.subscribe("akka-dispatcher", "**", subscriber, permanently = true)
+ Kamon.metrics.subscribe("counter", "**", subscriber, permanently = true)
+ Kamon.metrics.subscribe("histogram", "**", subscriber, permanently = true)
+ Kamon.metrics.subscribe("min-max-counter", "**", subscriber, permanently = true)
+ Kamon.metrics.subscribe("gauge", "**", subscriber, permanently = true)
val includeSystemMetrics = logReporterConfig.getBoolean("report-system-metrics")
if (includeSystemMetrics) {
@@ -47,6 +51,7 @@ class LogReporterExtension(system: ExtendedActorSystem) extends Kamon.Extension
}
class LogReporterSubscriber extends Actor with ActorLogging {
+
import kamon.logreporter.LogReporterSubscriber.RichHistogramSnapshot
def receive = {
@@ -54,13 +59,25 @@ class LogReporterSubscriber extends Actor with ActorLogging {
}
def printMetricSnapshot(tick: TickMetricSnapshot): Unit = {
+ // Group all the user metrics together.
+ val histograms = Map.newBuilder[String, Option[Histogram.Snapshot]]
+ val counters = Map.newBuilder[String, Option[Counter.Snapshot]]
+ val minMaxCounters = Map.newBuilder[String, Option[Histogram.Snapshot]]
+ val gauges = Map.newBuilder[String, Option[Histogram.Snapshot]]
+
tick.metrics foreach {
- case (entity, snapshot) if entity.category == "actor" ⇒ logActorMetrics(entity.name, snapshot)
- case (entity, snapshot) if entity.category == "trace" ⇒ logTraceMetrics(entity.name, snapshot)
- case (entity, snapshot) if entity.category == "simple-metric" ⇒ logSimpleMetrics(snapshot)
- case (entity, snapshot) if entity.category == "system-metric" ⇒ logSystemMetrics(entity.name, snapshot)
- case ignoreEverythingElse ⇒
+ case (entity, snapshot) if entity.category == "akka-actor" ⇒ logActorMetrics(entity.name, snapshot)
+ case (entity, snapshot) if entity.category == "akka-dispatcher" ⇒ logDispatcherMetrics(entity, snapshot)
+ case (entity, snapshot) if entity.category == "trace" ⇒ logTraceMetrics(entity.name, snapshot)
+ case (entity, snapshot) if entity.category == "histogram" ⇒ histograms += (entity.name -> snapshot.histogram("histogram"))
+ case (entity, snapshot) if entity.category == "counter" ⇒ counters += (entity.name -> snapshot.counter("counter"))
+ case (entity, snapshot) if entity.category == "min-max-counter" ⇒ minMaxCounters += (entity.name -> snapshot.minMaxCounter("min-max-counter"))
+ case (entity, snapshot) if entity.category == "gauge" ⇒ gauges += (entity.name -> snapshot.gauge("gauge"))
+ case (entity, snapshot) if entity.category == "system-metric" ⇒ logSystemMetrics(entity.name, snapshot)
+ case ignoreEverythingElse ⇒
}
+
+ logMetrics(histograms.result(), counters.result(), minMaxCounters.result(), gauges.result())
}
def logActorMetrics(name: String, actorSnapshot: EntitySnapshot): Unit = {
@@ -102,6 +119,79 @@ class LogReporterSubscriber extends Actor with ActorLogging {
}
+ def logDispatcherMetrics(entity: Entity, snapshot: EntitySnapshot): Unit = entity.tags.get("dispatcher-type") match {
+ case Some("fork-join-pool") ⇒ logForkJoinPool(entity.name, snapshot)
+ case Some("thread-pool-executor") ⇒ logThreadPoolExecutor(entity.name, snapshot)
+ case ignoreOthers ⇒
+ }
+
+ def logForkJoinPool(name: String, forkJoinMetrics: EntitySnapshot): Unit = {
+ for {
+ paralellism ← forkJoinMetrics.minMaxCounter("parallelism")
+ poolSize ← forkJoinMetrics.gauge("pool-size")
+ activeThreads ← forkJoinMetrics.gauge("active-threads")
+ runningThreads ← forkJoinMetrics.gauge("running-threads")
+ queuedTaskCount ← forkJoinMetrics.gauge("queued-task-count")
+
+ } {
+ log.info(
+ """
+ |+--------------------------------------------------------------------------------------------------+
+ || Fork-Join-Pool |
+ || |
+ || Dispatcher: %-83s |
+ || |
+ || Paralellism: %-4s |
+ || |
+ || Pool Size Active Threads Running Threads Queue Task Count |
+ || Min %-4s %-4s %-4s %-4s |
+ || Avg %-4s %-4s %-4s %-4s |
+ || Max %-4s %-4s %-4s %-4s |
+ || |
+ |+--------------------------------------------------------------------------------------------------+"""
+ .stripMargin.format(name,
+ paralellism.max, poolSize.min, activeThreads.min, runningThreads.min, queuedTaskCount.min,
+ poolSize.average, activeThreads.average, runningThreads.average, queuedTaskCount.average,
+ poolSize.max, activeThreads.max, runningThreads.max, queuedTaskCount.max))
+ }
+ }
+
+ def logThreadPoolExecutor(name: String, threadPoolMetrics: EntitySnapshot): Unit = {
+ for {
+ corePoolSize ← threadPoolMetrics.gauge("core-pool-size")
+ maxPoolSize ← threadPoolMetrics.gauge("max-pool-size")
+ poolSize ← threadPoolMetrics.gauge("pool-size")
+ activeThreads ← threadPoolMetrics.gauge("active-threads")
+ processedTasks ← threadPoolMetrics.gauge("processed-tasks")
+ } {
+
+ log.info(
+ """
+ |+--------------------------------------------------------------------------------------------------+
+ || Thread-Pool-Executor |
+ || |
+ || Dispatcher: %-83s |
+ || |
+ || Core Pool Size: %-4s |
+ || Max Pool Size: %-4s |
+ || |
+ || |
+ || Pool Size Active Threads Processed Task |
+ || Min %-4s %-4s %-4s |
+ || Avg %-4s %-4s %-4s |
+ || Max %-4s %-4s %-4s |
+ || |
+ |+--------------------------------------------------------------------------------------------------+"""
+ .stripMargin.format(name,
+ corePoolSize.max,
+ maxPoolSize.max,
+ poolSize.min, activeThreads.min, processedTasks.min,
+ poolSize.average, activeThreads.average, processedTasks.average,
+ poolSize.max, activeThreads.max, processedTasks.max))
+ }
+
+ }
+
def logSystemMetrics(metric: String, snapshot: EntitySnapshot): Unit = metric match {
case "cpu" ⇒ logCpuMetrics(snapshot)
case "network" ⇒ logNetworkMetrics(snapshot)
@@ -185,11 +275,10 @@ class LogReporterSubscriber extends Actor with ActorLogging {
|| Max: %-12s Max: %-12s |
|| |
|+--------------------------------------------------------------------------------------------------+"""
- .stripMargin.
- format(
- (user.min, total.min,
- user.average, total.average,
- user.max, total.max)))
+ .stripMargin.format(
+ user.min, total.min,
+ user.average, total.average,
+ user.max, total.max))
}
}
@@ -253,78 +342,74 @@ class LogReporterSubscriber extends Actor with ActorLogging {
}
}
- def logSimpleMetrics(simpleMetrics: EntitySnapshot): Unit = {
- val histograms = simpleMetrics.histograms
- val minMaxCounters = simpleMetrics.minMaxCounters
- val gauges = simpleMetrics.gauges
- val counters = simpleMetrics.counters
+ def logMetrics(histograms: Map[String, Option[Histogram.Snapshot]],
+ counters: Map[String, Option[Counter.Snapshot]], minMaxCounters: Map[String, Option[Histogram.Snapshot]],
+ gauges: Map[String, Option[Histogram.Snapshot]]): Unit = {
if (histograms.isEmpty && counters.isEmpty && minMaxCounters.isEmpty && gauges.isEmpty) {
- log.info("No user metrics reported")
+ log.info("No metrics reported")
return
}
- val simpleMetricsData = StringBuilder.newBuilder
+ val metricsData = StringBuilder.newBuilder
- simpleMetricsData.append(
+ metricsData.append(
"""
|+--------------------------------------------------------------------------------------------------+
|| |
- || Simple Counters |
+ || Counters |
|| ------------- |
|""".stripMargin)
- counters.toList.sortBy(_._1.name.toLowerCase).foreach {
- case (counter, snapshot) ⇒ simpleMetricsData.append(userCounterString(counter.name, snapshot))
- }
+ counters.foreach { case (name, snapshot) ⇒ metricsData.append(userCounterString(name, snapshot.get)) }
- simpleMetricsData.append(
+ metricsData.append(
"""|| |
|| |
- || Simple Histograms |
- || --------------- |
+ || Histograms |
+ || -------------- |
|""".stripMargin)
histograms.foreach {
- case (histogram, snapshot) ⇒
- simpleMetricsData.append("| %-40s |\n".format(histogram.name))
- simpleMetricsData.append(compactHistogramView(snapshot))
- simpleMetricsData.append("\n| |\n")
+ case (name, snapshot) ⇒
+ metricsData.append("| %-40s |\n".format(name))
+ metricsData.append(compactHistogramView(snapshot.get))
+ metricsData.append("\n| |\n")
}
- simpleMetricsData.append(
+ metricsData.append(
"""|| |
- || Simple MinMaxCounters |
- || ------------------- |
+ || MinMaxCounters |
+ || ----------------- |
|""".stripMargin)
minMaxCounters.foreach {
- case (minMaxCounter, snapshot) ⇒
- simpleMetricsData.append("| %-40s |\n".format(minMaxCounter.name))
- simpleMetricsData.append(simpleHistogramView(snapshot))
- simpleMetricsData.append("\n| |\n")
+ case (name, snapshot) ⇒
+ metricsData.append("| %-40s |\n".format(name))
+ metricsData.append(histogramView(snapshot.get))
+ metricsData.append("\n| |\n")
}
- simpleMetricsData.append(
+ metricsData.append(
"""|| |
- || User Gauges |
- || ----------- |
+ || Gauges |
+ || ---------- |
|"""
.stripMargin)
gauges.foreach {
- case (gauge, snapshot) ⇒
- simpleMetricsData.append("| %-40s |\n".format(gauge.name))
- simpleMetricsData.append(simpleHistogramView(snapshot))
- simpleMetricsData.append("\n| |\n")
+ case (name, snapshot) ⇒
+ metricsData.append("| %-40s |\n".format(name))
+ metricsData.append(histogramView(snapshot.get))
+ metricsData.append("\n| |\n")
}
- simpleMetricsData.append(
+ metricsData.append(
"""|| |
|+--------------------------------------------------------------------------------------------------+"""
.stripMargin)
- log.info(simpleMetricsData.toString())
+ log.info(metricsData.toString())
}
def userCounterString(counterName: String, snapshot: Counter.Snapshot): String = {
@@ -343,7 +428,7 @@ class LogReporterSubscriber extends Actor with ActorLogging {
sb.toString()
}
- def simpleHistogramView(histogram: Histogram.Snapshot): String =
+ def histogramView(histogram: Histogram.Snapshot): String =
"| Min: %-12s Average: %-12s Max: %-12s |"
.format(histogram.min, histogram.average, histogram.max)
}
diff --git a/kamon-newrelic/src/main/scala/kamon/newrelic/CustomMetricExtractor.scala b/kamon-newrelic/src/main/scala/kamon/newrelic/CustomMetricExtractor.scala
index 41ed9661..6919a967 100644
--- a/kamon-newrelic/src/main/scala/kamon/newrelic/CustomMetricExtractor.scala
+++ b/kamon-newrelic/src/main/scala/kamon/newrelic/CustomMetricExtractor.scala
@@ -16,17 +16,22 @@
package kamon.newrelic
-import kamon.metric.{ SimpleMetricsImpl, EntitySnapshot, Entity }
+import kamon.metric.{ 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(SimpleMetricsImpl.SimpleMetricsEntity).map { allSimpleMetrics ⇒
- allSimpleMetrics.metrics.map {
- case (key, snapshot) ⇒ Metric(snapshot, key.unitOfMeasurement, s"Custom/${key.name}", None)
- }
+ def onlySimpleMetrics(kv: (Entity, EntitySnapshot)): Boolean =
+ kamon.metric.SingleInstrumentEntityRecorder.AllCategories.contains(kv._1.category)
- } getOrElse (Map.empty)
+ def toNewRelicMetric(kv: (Entity, EntitySnapshot)): (MetricID, MetricData) = {
+ val (entity, entitySnapshot) = kv
+ val (metricKey, instrumentSnapshot) = entitySnapshot.metrics.head
+
+ Metric(instrumentSnapshot, metricKey.unitOfMeasurement, s"Custom/${entity.name}", None)
+ }
+
+ metrics.filter(onlySimpleMetrics).map(toNewRelicMetric)
}
}
diff --git a/kamon-newrelic/src/main/scala/kamon/newrelic/Metric.scala b/kamon-newrelic/src/main/scala/kamon/newrelic/Metric.scala
index 20204b79..6fb645f5 100644
--- a/kamon-newrelic/src/main/scala/kamon/newrelic/Metric.scala
+++ b/kamon-newrelic/src/main/scala/kamon/newrelic/Metric.scala
@@ -18,9 +18,9 @@ case class MetricData(callCount: Long, total: Double, totalExclusive: Double, mi
object Metric {
- def scaleFunction(uom: UnitOfMeasurement): Long ⇒ Double = uom match {
+ def scaleFunction(uom: UnitOfMeasurement): Double ⇒ Double = uom match {
case time: Time ⇒ time.scale(Time.Seconds)
- case other ⇒ _.toDouble
+ case other ⇒ a ⇒ a
}
def apply(snapshot: InstrumentSnapshot, snapshotUnit: UnitOfMeasurement, name: String, scope: Option[String]): Metric = {
diff --git a/kamon-newrelic/src/main/scala/kamon/newrelic/WebTransactionMetricExtractor.scala b/kamon-newrelic/src/main/scala/kamon/newrelic/WebTransactionMetricExtractor.scala
index d0144f4b..76cf0757 100644
--- a/kamon-newrelic/src/main/scala/kamon/newrelic/WebTransactionMetricExtractor.scala
+++ b/kamon-newrelic/src/main/scala/kamon/newrelic/WebTransactionMetricExtractor.scala
@@ -17,6 +17,7 @@
package kamon.newrelic
import kamon.metric.{ EntitySnapshot, Entity }
+import kamon.trace.SegmentCategory
import scala.collection.mutable
import kamon.metric.instrument.{ Time, CollectionContext, Histogram }
@@ -35,38 +36,36 @@ object WebTransactionMetricExtractor extends MetricExtractor {
val externalScopedByHostAndLibrarySnapshots = mutable.Map.empty[(String, String, String), List[Histogram.Snapshot]]
val transactionMetrics = metrics.filterKeys(_.category == "trace").map {
- case (entity: Entity, es: EntitySnapshot) ⇒
- // Trace metrics only have elapsed-time and segments and all of them are Histograms.
- es.histograms.foreach {
- case (key, segmentSnapshot) if key.metadata.get("category").filter(_ == "http-client").nonEmpty ⇒
- val library = key.metadata("library")
- accumulatedExternalServices = accumulatedExternalServices.merge(segmentSnapshot, collectionContext)
-
- // Accumulate externals by host
- externalByHostSnapshots.update(key.name, segmentSnapshot :: externalByHostSnapshots.getOrElse(key.name, Nil))
+ case (entity, entitySnapshot) ⇒
+ val elapsedTime = entitySnapshot.histogram("elapsed-time").get
+ accumulatedHttpDispatcher = accumulatedHttpDispatcher.merge(elapsedTime, collectionContext)
+ elapsedTime.recordsIterator.foreach { record ⇒
+ apdexBuilder.record(Time.Nanoseconds.scale(Time.Seconds)(record.level), record.count)
+ }
- // Accumulate externals by host and library
- externalByHostAndLibrarySnapshots.update((key.name, library),
- segmentSnapshot :: externalByHostAndLibrarySnapshots.getOrElse((key.name, library), Nil))
+ Metric(elapsedTime, Time.Nanoseconds, "WebTransaction/Custom/" + entity.name, None)
+ }
- // Accumulate externals by host and library, including the transaction as scope.
- externalScopedByHostAndLibrarySnapshots.update((key.name, library, entity.name),
- segmentSnapshot :: externalScopedByHostAndLibrarySnapshots.getOrElse((key.name, library, entity.name), Nil))
+ // Accumulate all segment metrics
+ metrics.filterKeys(_.category == "trace-segment").map {
+ case (entity, entitySnapshot) if entity.tags("category") == SegmentCategory.HttpClient ⇒
+ val library = entity.tags("library")
+ val trace = entity.tags("trace")
+ val elapsedTime = entitySnapshot.histogram("elapsed-time").get
- case otherSegments ⇒
+ accumulatedExternalServices = accumulatedExternalServices.merge(elapsedTime, collectionContext)
- }
+ // Accumulate externals by host
+ externalByHostSnapshots.update(entity.name, elapsedTime :: externalByHostSnapshots.getOrElse(entity.name, Nil))
- es.histograms.collect {
- case (key, elapsedTime) if key.name == "elapsed-time" ⇒
- accumulatedHttpDispatcher = accumulatedHttpDispatcher.merge(elapsedTime, collectionContext)
- elapsedTime.recordsIterator.foreach { record ⇒
- apdexBuilder.record(Time.Nanoseconds.scale(Time.Seconds)(record.level), record.count)
- }
+ // Accumulate externals by host and library
+ externalByHostAndLibrarySnapshots.update((entity.name, library),
+ elapsedTime :: externalByHostAndLibrarySnapshots.getOrElse((entity.name, library), Nil))
- Metric(elapsedTime, key.unitOfMeasurement, "WebTransaction/Custom/" + entity.name, None)
- }
- } flatten
+ // Accumulate externals by host and library, including the transaction as scope.
+ externalScopedByHostAndLibrarySnapshots.update((entity.name, library, trace),
+ elapsedTime :: externalScopedByHostAndLibrarySnapshots.getOrElse((entity.name, library, trace), Nil))
+ }
val httpDispatcher = Metric(accumulatedHttpDispatcher, Time.Seconds, "HttpDispatcher", None)
val webTransaction = httpDispatcher.copy(MetricID("WebTransaction", None))
diff --git a/kamon-newrelic/src/test/scala/kamon/newrelic/MetricReporterSpec.scala b/kamon-newrelic/src/test/scala/kamon/newrelic/MetricReporterSpec.scala
index 04380677..d4e815e5 100644
--- a/kamon-newrelic/src/test/scala/kamon/newrelic/MetricReporterSpec.scala
+++ b/kamon-newrelic/src/test/scala/kamon/newrelic/MetricReporterSpec.scala
@@ -139,19 +139,19 @@ class MetricReporterSpec extends BaseKamonSpec("metric-reporter-spec") with Spra
trait FakeTickSnapshotsFixture {
val testTraceID = Entity("example-trace", "trace")
- val recorder = Kamon.metrics.register(TraceMetrics, testTraceID.name).get.recorder
+ val recorder = Kamon.metrics.entity(TraceMetrics, testTraceID.name)
val collectionContext = Kamon.metrics.buildDefaultCollectionContext
def collectRecorder = recorder.collect(collectionContext)
- recorder.ElapsedTime.record(1000000)
- recorder.ElapsedTime.record(2000000)
- recorder.ElapsedTime.record(3000000)
+ recorder.elapsedTime.record(1000000)
+ recorder.elapsedTime.record(2000000)
+ recorder.elapsedTime.record(3000000)
val firstSnapshot = TickMetricSnapshot(new MilliTimestamp(1415587618000L), new MilliTimestamp(1415587678000L), Map(testTraceID -> collectRecorder))
- recorder.ElapsedTime.record(6000000)
- recorder.ElapsedTime.record(5000000)
- recorder.ElapsedTime.record(4000000)
+ recorder.elapsedTime.record(6000000)
+ recorder.elapsedTime.record(5000000)
+ recorder.elapsedTime.record(4000000)
val secondSnapshot = TickMetricSnapshot(new MilliTimestamp(1415587678000L), new MilliTimestamp(1415587738000L), Map(testTraceID -> collectRecorder))
}
} \ No newline at end of file
diff --git a/kamon-play/src/main/scala/kamon/play/Play.scala b/kamon-play/src/main/scala/kamon/play/Play.scala
index 9e160d58..270d244f 100644
--- a/kamon-play/src/main/scala/kamon/play/Play.scala
+++ b/kamon-play/src/main/scala/kamon/play/Play.scala
@@ -36,13 +36,7 @@ class PlayExtension(private val system: ExtendedActorSystem) extends Kamon.Exten
log.info(s"Starting the Kamon(Play) extension")
private val config = system.settings.config.getConfig("kamon.play")
- val httpServerMetrics = {
- val metricsExtension = Kamon.metrics
- val factory = metricsExtension.instrumentFactory(HttpServerMetrics.category)
- val entity = Entity("play-server", HttpServerMetrics.category)
-
- metricsExtension.register(entity, new HttpServerMetrics(factory)).recorder
- }
+ val httpServerMetrics = Kamon.metrics.entity(HttpServerMetrics, "play-server")
val defaultDispatcher = system.dispatcher
val includeTraceToken: Boolean = config.getBoolean("automatic-trace-token-propagation")
diff --git a/kamon-play/src/test/scala/kamon/play/WSInstrumentationSpec.scala b/kamon-play/src/test/scala/kamon/play/WSInstrumentationSpec.scala
index d639fd9f..fe16ccbf 100644
--- a/kamon-play/src/test/scala/kamon/play/WSInstrumentationSpec.scala
+++ b/kamon-play/src/test/scala/kamon/play/WSInstrumentationSpec.scala
@@ -17,7 +17,7 @@
package kamon.play
import kamon.Kamon
-import kamon.metric.{ EntitySnapshot, TraceMetrics }
+import kamon.metric.{ Entity, EntitySnapshot, TraceMetrics }
import kamon.trace.{ Tracer, TraceContext, SegmentCategory }
import org.scalatest.{ Matchers, WordSpecLike }
import org.scalatestplus.play.OneServerPerSuite
@@ -32,7 +32,7 @@ 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")
override lazy val port: Port = 19003
@@ -46,10 +46,16 @@ class WSInstrumentationSpec extends WordSpecLike with Matchers with OneServerPer
"propagate the TraceContext inside an Action and complete the WS request" in {
Await.result(route(FakeRequest(GET, "/inside")).get, 10 seconds)
- val snapshot = takeSnapshotOf("GET: /inside")
+ val snapshot = takeSnapshotOf("GET: /inside", "trace")
snapshot.histogram("elapsed-time").get.numberOfMeasurements should be(1)
- snapshot.segments.size should be(1)
- snapshot.segment(s"http://localhost:$port/async", SegmentCategory.HttpClient, Play.SegmentLibraryName).numberOfMeasurements should be(1)
+
+ val segmentMetricsSnapshot = takeSnapshotOf(s"http://localhost:$port/async", "trace-segment",
+ tags = Map(
+ "trace" -> "GET: /inside",
+ "category" -> SegmentCategory.HttpClient,
+ "library" -> Play.SegmentLibraryName))
+
+ segmentMetricsSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(1)
}
"propagate the TraceContext outside an Action and complete the WS request" in {
@@ -58,22 +64,26 @@ class WSInstrumentationSpec extends WordSpecLike with Matchers with OneServerPer
Tracer.currentContext.finish()
}
- val snapshot = takeSnapshotOf("trace-outside-action")
+ val snapshot = takeSnapshotOf("trace-outside-action", "trace")
snapshot.histogram("elapsed-time").get.numberOfMeasurements should be(1)
- snapshot.segments.size should be(1)
- snapshot.segment(s"http://localhost:$port/outside", SegmentCategory.HttpClient, Play.SegmentLibraryName).numberOfMeasurements should be(1)
- }
+ val segmentMetricsSnapshot = takeSnapshotOf(s"http://localhost:$port/outside", "trace-segment",
+ tags = Map(
+ "trace" -> "trace-outside-action",
+ "category" -> SegmentCategory.HttpClient,
+ "library" -> Play.SegmentLibraryName))
+
+ segmentMetricsSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(1)
+ }
}
+ lazy val collectionContext = Kamon.metrics.buildDefaultCollectionContext
+
def newContext(name: String): TraceContext =
Kamon.tracer.newContext(name)
- def takeSnapshotOf(traceName: String): EntitySnapshot = {
- // Give some time for async segments to finish.
- Thread.sleep(300)
- val recorder = Kamon.metrics.register(TraceMetrics, traceName).get.recorder
- val collectionContext = Kamon.metrics.buildDefaultCollectionContext
+ def takeSnapshotOf(name: String, category: String, tags: Map[String, String] = Map.empty): EntitySnapshot = {
+ val recorder = Kamon.metrics.find(Entity(name, category, tags)).get
recorder.collect(collectionContext)
}
diff --git a/kamon-playground/src/main/resources/application.conf b/kamon-playground/src/main/resources/application.conf
index 74e710bc..463894b2 100644
--- a/kamon-playground/src/main/resources/application.conf
+++ b/kamon-playground/src/main/resources/application.conf
@@ -22,7 +22,8 @@ kamon {
metric {
filters {
trace.includes = [ "**" ]
- actor.includes = [ "**" ]
+ akka-actor.includes = [ "**" ]
+ akka-dispatcher.includes = [ "**" ]
}
}
@@ -31,10 +32,14 @@ kamon {
license-key = e7d350b14228f3d28f35bc3140df2c3e565ea5d5
}
+ internal-config {
+ akka.loglevel = DEBUG
+ }
+
modules {
kamon-newrelic.auto-start = no
kamon-datadog.auto-start = no
- kamon-log-reporter.auto-start = no
+ kamon-log-reporter.auto-start = yes
kamon-system-metrics.auto-start = no
}
}
diff --git a/kamon-playground/src/main/scala/test/SimpleRequestProcessor.scala b/kamon-playground/src/main/scala/test/SimpleRequestProcessor.scala
index 141c8768..280158c0 100644
--- a/kamon-playground/src/main/scala/test/SimpleRequestProcessor.scala
+++ b/kamon-playground/src/main/scala/test/SimpleRequestProcessor.scala
@@ -37,8 +37,8 @@ object SimpleRequestProcessor extends App with SimpleRoutingApp with RequestBuil
import scala.concurrent.duration._
- implicit val system = ActorSystem("test")
Kamon.start()
+ implicit val system = ActorSystem("test")
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.simpleMetrics.counter("requests")
+ val counter = Kamon.metrics.counter("requests")
val pipeline = sendReceive
val replier = system.actorOf(Props[Replier].withRouter(RoundRobinPool(nrOfInstances = 4)), "replier")
diff --git a/kamon-spray/src/main/scala/kamon/spray/SprayExtension.scala b/kamon-spray/src/main/scala/kamon/spray/SprayExtension.scala
index ab0fe50b..a5aa33cb 100644
--- a/kamon-spray/src/main/scala/kamon/spray/SprayExtension.scala
+++ b/kamon-spray/src/main/scala/kamon/spray/SprayExtension.scala
@@ -46,11 +46,8 @@ class SprayExtensionImpl(system: ExtendedActorSystem) extends SprayExtension {
val log = Logging(system, "SprayExtension")
val httpServerMetrics = {
- val metricsExtension = Kamon.metrics
- val factory = metricsExtension.instrumentFactory(HttpServerMetrics.category)
val entity = Entity("spray-server", HttpServerMetrics.category)
-
- metricsExtension.register(entity, new HttpServerMetrics(factory)).recorder
+ Kamon.metrics.entity(HttpServerMetrics, entity)
}
def generateTraceName(request: HttpRequest): String =
diff --git a/kamon-spray/src/test/scala/kamon/spray/ClientRequestInstrumentationSpec.scala b/kamon-spray/src/test/scala/kamon/spray/ClientRequestInstrumentationSpec.scala
index 67e6725f..4b99022e 100644
--- a/kamon-spray/src/test/scala/kamon/spray/ClientRequestInstrumentationSpec.scala
+++ b/kamon-spray/src/test/scala/kamon/spray/ClientRequestInstrumentationSpec.scala
@@ -34,8 +34,6 @@ import scala.concurrent.duration._
class ClientRequestInstrumentationSpec extends BaseKamonSpec("client-request-instrumentation-spec") with ScalaFutures
with RequestBuilding with TestServer {
- import TraceMetricsSpec.SegmentSyntax
-
override lazy val config =
ConfigFactory.parseString(
"""
@@ -130,8 +128,14 @@ class ClientRequestInstrumentationSpec extends BaseKamonSpec("client-request-ins
val traceMetricsSnapshot = takeSnapshotOf("assign-name-to-segment-with-request-level-api", "trace")
traceMetricsSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(1)
- traceMetricsSnapshot.segment("request-level /request-level-api-segment", SegmentCategory.HttpClient, Spray.SegmentLibraryName)
- .numberOfMeasurements should be(1)
+
+ val segmentMetricsSnapshot = takeSnapshotOf("request-level /request-level-api-segment", "trace-segment",
+ tags = Map(
+ "trace" -> "assign-name-to-segment-with-request-level-api",
+ "category" -> SegmentCategory.HttpClient,
+ "library" -> Spray.SegmentLibraryName))
+
+ segmentMetricsSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(1)
}
"rename a request level api segment once it reaches the relevant host connector" in {
@@ -161,8 +165,14 @@ class ClientRequestInstrumentationSpec extends BaseKamonSpec("client-request-ins
val traceMetricsSnapshot = takeSnapshotOf("rename-segment-with-request-level-api", "trace")
traceMetricsSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(1)
- traceMetricsSnapshot.segment("host-level /request-level-api-segment", SegmentCategory.HttpClient, Spray.SegmentLibraryName)
- .numberOfMeasurements should be(1)
+
+ val segmentMetricsSnapshot = takeSnapshotOf("host-level /request-level-api-segment", "trace-segment",
+ tags = Map(
+ "trace" -> "rename-segment-with-request-level-api",
+ "category" -> SegmentCategory.HttpClient,
+ "library" -> Spray.SegmentLibraryName))
+
+ segmentMetricsSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(1)
}
}
@@ -249,8 +259,14 @@ class ClientRequestInstrumentationSpec extends BaseKamonSpec("client-request-ins
val traceMetricsSnapshot = takeSnapshotOf("create-segment-with-host-level-api", "trace")
traceMetricsSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(1)
- traceMetricsSnapshot.segment("host-level /host-level-api-segment", SegmentCategory.HttpClient, Spray.SegmentLibraryName)
- .numberOfMeasurements should be(1)
+
+ val segmentMetricsSnapshot = takeSnapshotOf("host-level /host-level-api-segment", "trace-segment",
+ tags = Map(
+ "trace" -> "create-segment-with-host-level-api",
+ "category" -> SegmentCategory.HttpClient,
+ "library" -> Spray.SegmentLibraryName))
+
+ segmentMetricsSnapshot.histogram("elapsed-time").get.numberOfMeasurements should be(1)
}
}
}
diff --git a/kamon-statsd/src/main/resources/reference.conf b/kamon-statsd/src/main/resources/reference.conf
index f26ce98b..63a784a4 100644
--- a/kamon-statsd/src/main/resources/reference.conf
+++ b/kamon-statsd/src/main/resources/reference.conf
@@ -20,11 +20,16 @@ kamon {
# Subscription patterns used to select which metrics will be pushed to StatsD. Note that first, metrics
# collection for your desired entities must be activated under the kamon.metrics.filters settings.
subscriptions {
- trace = [ "**" ]
- actor = [ "**" ]
- dispatcher = [ "**" ]
- user-metric = [ "**" ]
- system-metric = [ "**" ]
+ histogram = [ "**" ]
+ min-max-counter = [ "**" ]
+ gauge = [ "**" ]
+ counter = [ "**" ]
+ trace = [ "**" ]
+ trace-segment = [ "**" ]
+ akka-actor = [ "**" ]
+ akka-dispatcher = [ "**" ]
+ akka-router = [ "**" ]
+ system-metric = [ "**" ]
}
# Enable system metrics
diff --git a/kamon-statsd/src/main/scala/kamon/statsd/SimpleMetricKeyGenerator.scala b/kamon-statsd/src/main/scala/kamon/statsd/SimpleMetricKeyGenerator.scala
index 0fce855c..97a27ff3 100644
--- a/kamon-statsd/src/main/scala/kamon/statsd/SimpleMetricKeyGenerator.scala
+++ b/kamon-statsd/src/main/scala/kamon/statsd/SimpleMetricKeyGenerator.scala
@@ -3,7 +3,7 @@ package kamon.statsd
import java.lang.management.ManagementFactory
import com.typesafe.config.Config
-import kamon.metric.{ MetricKey, Entity }
+import kamon.metric.{ SingleInstrumentEntityRecorder, MetricKey, Entity }
trait MetricKeyGenerator {
def generateKey(entity: Entity, metricKey: MetricKey): String
@@ -26,9 +26,16 @@ class SimpleMetricKeyGenerator(config: Config) extends MetricKeyGenerator {
if (includeHostname) s"$application.$normalizedHostname"
else application
- def generateKey(entity: Entity, metricKey: MetricKey): String = {
- val normalizedGroupName = normalizer(entity.name)
- s"${baseName}.${entity.category}.${normalizedGroupName}.${metricKey.name}"
+ def generateKey(entity: Entity, metricKey: MetricKey): String = entity.category match {
+ case "trace-segment" ⇒
+ s"${baseName}.trace.${normalizer(entity.tags("trace"))}.segments.${normalizer(entity.name)}.${metricKey.name}"
+
+ case _ if SingleInstrumentEntityRecorder.AllCategories.contains(entity.category) ⇒
+ s"${baseName}.${entity.category}.${normalizer(entity.name)}"
+
+ case _ ⇒
+ s"${baseName}.${entity.category}.${normalizer(entity.name)}.${metricKey.name}"
+
}
def hostName: String = ManagementFactory.getRuntimeMXBean.getName.split('@')(1)
diff --git a/kamon-statsd/src/main/scala/kamon/statsd/StatsD.scala b/kamon-statsd/src/main/scala/kamon/statsd/StatsD.scala
index d406faf6..91d05510 100644
--- a/kamon-statsd/src/main/scala/kamon/statsd/StatsD.scala
+++ b/kamon-statsd/src/main/scala/kamon/statsd/StatsD.scala
@@ -43,7 +43,6 @@ class StatsDExtension(system: ExtendedActorSystem) extends Kamon.Extension {
val metricsExtension = Kamon.metrics
val tickInterval = metricsExtension.settings.tickInterval
- val statsDHost = new InetSocketAddress(statsDConfig.getString("hostname"), statsDConfig.getInt("port"))
val flushInterval = statsDConfig.getFiniteDuration("flush-interval")
val maxPacketSizeInBytes = statsDConfig.getBytes("max-packet-size")
val keyGeneratorFQCN = statsDConfig.getString("metric-key-generator")
@@ -62,7 +61,8 @@ class StatsDExtension(system: ExtendedActorSystem) extends Kamon.Extension {
val keyGenerator = system.dynamicAccess.createInstanceFor[MetricKeyGenerator](keyGeneratorFQCN, (classOf[Config], config) :: Nil).get
val metricsSender = system.actorOf(StatsDMetricsSender.props(
- statsDHost,
+ statsDConfig.getString("hostname"),
+ statsDConfig.getInt("port"),
maxPacketSizeInBytes,
keyGenerator), "statsd-metrics-sender")
diff --git a/kamon-statsd/src/main/scala/kamon/statsd/StatsDMetricsSender.scala b/kamon-statsd/src/main/scala/kamon/statsd/StatsDMetricsSender.scala
index 3241e1f3..70ff1b45 100644
--- a/kamon-statsd/src/main/scala/kamon/statsd/StatsDMetricsSender.scala
+++ b/kamon-statsd/src/main/scala/kamon/statsd/StatsDMetricsSender.scala
@@ -26,7 +26,7 @@ import java.util.Locale
import kamon.metric.instrument.{ Counter, Histogram }
-class StatsDMetricsSender(remote: InetSocketAddress, maxPacketSizeInBytes: Long, metricKeyGenerator: MetricKeyGenerator)
+class StatsDMetricsSender(statsDHost: String, statsDPort: Int, maxPacketSizeInBytes: Long, metricKeyGenerator: MetricKeyGenerator)
extends Actor with UdpExtensionProvider {
import context.system
@@ -38,6 +38,8 @@ class StatsDMetricsSender(remote: InetSocketAddress, maxPacketSizeInBytes: Long,
udpExtension ! Udp.SimpleSender
+ def newSocketAddress = new InetSocketAddress(statsDHost, statsDPort)
+
def receive = {
case Udp.SimpleSenderReady ⇒
context.become(ready(sender))
@@ -48,7 +50,7 @@ class StatsDMetricsSender(remote: InetSocketAddress, maxPacketSizeInBytes: Long,
}
def writeMetricsToRemote(tick: TickMetricSnapshot, udpSender: ActorRef): Unit = {
- val packetBuilder = new MetricDataPacketBuilder(maxPacketSizeInBytes, udpSender, remote)
+ val packetBuilder = new MetricDataPacketBuilder(maxPacketSizeInBytes, udpSender, newSocketAddress)
for (
(entity, snapshot) ← tick.metrics;
@@ -80,8 +82,8 @@ class StatsDMetricsSender(remote: InetSocketAddress, maxPacketSizeInBytes: Long,
}
object StatsDMetricsSender {
- def props(remote: InetSocketAddress, maxPacketSize: Long, metricKeyGenerator: MetricKeyGenerator): Props =
- Props(new StatsDMetricsSender(remote, maxPacketSize, metricKeyGenerator))
+ def props(statsDHost: String, statsDPort: Int, maxPacketSize: Long, metricKeyGenerator: MetricKeyGenerator): Props =
+ Props(new StatsDMetricsSender(statsDHost, statsDPort, maxPacketSize, metricKeyGenerator))
}
trait UdpExtensionProvider {
diff --git a/kamon-statsd/src/test/scala/kamon/statsd/SimpleMetricKeyGeneratorSpec.scala b/kamon-statsd/src/test/scala/kamon/statsd/SimpleMetricKeyGeneratorSpec.scala
index 0edeb3df..2e03a59d 100644
--- a/kamon-statsd/src/test/scala/kamon/statsd/SimpleMetricKeyGeneratorSpec.scala
+++ b/kamon-statsd/src/test/scala/kamon/statsd/SimpleMetricKeyGeneratorSpec.scala
@@ -69,7 +69,7 @@ class SimpleMetricKeyGeneratorSpec extends WordSpec with Matchers {
}
def buildMetricKey(categoryName: String, entityName: String, metricName: String)(implicit metricKeyGenerator: SimpleMetricKeyGenerator): String = {
- val metric = HistogramKey(metricName, UnitOfMeasurement.Unknown, Map.empty)
+ val metric = HistogramKey(metricName, UnitOfMeasurement.Unknown)
val entity = Entity(entityName, categoryName)
metricKeyGenerator.generateKey(entity, metric)
}
diff --git a/kamon-statsd/src/test/scala/kamon/statsd/StatsDMetricSenderSpec.scala b/kamon-statsd/src/test/scala/kamon/statsd/StatsDMetricSenderSpec.scala
index 0211ac0f..51e7cf19 100644
--- a/kamon-statsd/src/test/scala/kamon/statsd/StatsDMetricSenderSpec.scala
+++ b/kamon-statsd/src/test/scala/kamon/statsd/StatsDMetricSenderSpec.scala
@@ -128,17 +128,17 @@ class StatsDMetricSenderSpec extends BaseKamonSpec("statsd-metric-sender-spec")
val testEntity = Entity("user/kamon", "test")
def buildMetricKey(entity: Entity, metricName: String)(implicit metricKeyGenerator: SimpleMetricKeyGenerator): String = {
- val metricKey = HistogramKey(metricName, UnitOfMeasurement.Unknown, Map.empty)
+ val metricKey = HistogramKey(metricName, UnitOfMeasurement.Unknown)
metricKeyGenerator.generateKey(entity, metricKey)
}
def buildRecorder(name: String): TestEntityRecorder = {
- Kamon.metrics.register(TestEntityRecorder, name).get.recorder
+ Kamon.metrics.entity(TestEntityRecorder, name)
}
def setup(metrics: Map[Entity, EntitySnapshot]): TestProbe = {
val udp = TestProbe()
- val metricsSender = system.actorOf(Props(new StatsDMetricsSender(new InetSocketAddress("127.0.0.1", 0), testMaxPacketSize, metricKeyGenerator) {
+ val metricsSender = system.actorOf(Props(new StatsDMetricsSender("127.0.0.1", 0, testMaxPacketSize, metricKeyGenerator) {
override def udpExtension(implicit system: ActorSystem): ActorRef = udp.ref
}))
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 ebdaf01f..94c6fefa 100644
--- a/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsExtension.scala
+++ b/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsExtension.scala
@@ -16,18 +16,19 @@
package kamon.system
import java.io.File
+import akka.actor.SupervisorStrategy.Restart
import akka.actor._
import akka.event.Logging
-import kamon.supervisor.ModuleSupervisor
import kamon.system.custom.{ ContextSwitchesUpdater, ContextSwitchesMetrics }
import kamon.system.jmx._
import kamon.Kamon
-import kamon.metric._
import kamon.sigar.SigarProvisioner
import kamon.system.sigar.SigarMetricsUpdater
import kamon.util.ConfigTools.Syntax
+import scala.concurrent.duration.FiniteDuration
+
object SystemMetrics extends ExtensionId[SystemMetricsExtension] with ExtensionIdProvider {
override def lookup(): ExtensionId[_ <: Extension] = SystemMetrics
override def createExtension(system: ExtendedActorSystem): SystemMetricsExtension = new SystemMetricsExtension(system)
@@ -44,10 +45,9 @@ class SystemMetricsExtension(system: ExtendedActorSystem) extends Kamon.Extensio
val contextSwitchesRefreshInterval = config.getFiniteDuration("context-switches-refresh-interval")
val metricsExtension = Kamon.metrics
- // Sigar-based metrics
SigarProvisioner.provision(new File(sigarFolder))
- val sigarMetricsRecorder = ModuleSupervisor.get(system).createModule("sigar-metrics-recorder",
- SigarMetricsUpdater.props(sigarRefreshInterval).withDispatcher("kamon.system-metrics.sigar-dispatcher"))
+
+ val supervisor = system.actorOf(SystemMetricsSupervisor.props(sigarRefreshInterval, contextSwitchesRefreshInterval), "kamon-system-metrics")
// JMX Metrics
ClassLoadingMetrics.register(metricsExtension)
@@ -55,17 +55,32 @@ class SystemMetricsExtension(system: ExtendedActorSystem) extends Kamon.Extensio
HeapMemoryMetrics.register(metricsExtension)
NonHeapMemoryMetrics.register(metricsExtension)
ThreadsMetrics.register(metricsExtension)
+}
+
+class SystemMetricsSupervisor(sigarRefreshInterval: FiniteDuration, contextSwitchesRefreshInterval: FiniteDuration) extends Actor {
+
+ // Sigar metrics recorder
+ context.actorOf(SigarMetricsUpdater.props(sigarRefreshInterval)
+ .withDispatcher("kamon.system-metrics.sigar-dispatcher"), "sigar-metrics-recorder")
// If we are in Linux, add ContextSwitchesMetrics as well.
if (isLinux) {
- val contextSwitchesRecorder = ContextSwitchesMetrics.register(system, contextSwitchesRefreshInterval)
-
- ModuleSupervisor.get(system).createModule("context-switches-metrics-recorder",
- ContextSwitchesUpdater.props(contextSwitchesRecorder, sigarRefreshInterval)
- .withDispatcher("kamon.system-metrics.context-switches-dispatcher"))
+ val contextSwitchesRecorder = ContextSwitchesMetrics.register(context.system, contextSwitchesRefreshInterval)
+ context.actorOf(ContextSwitchesUpdater.props(contextSwitchesRecorder, sigarRefreshInterval)
+ .withDispatcher("kamon.system-metrics.context-switches-dispatcher"), "context-switches-metrics-recorder")
}
def isLinux: Boolean =
System.getProperty("os.name").indexOf("Linux") != -1
+ def receive = Actor.emptyBehavior
+
+ override def supervisorStrategy: SupervisorStrategy = OneForOneStrategy() {
+ case anyException ⇒ Restart
+ }
+}
+
+object SystemMetricsSupervisor {
+ def props(sigarRefreshInterval: FiniteDuration, contextSwitchesRefreshInterval: FiniteDuration): Props =
+ Props(new SystemMetricsSupervisor(sigarRefreshInterval, contextSwitchesRefreshInterval))
} \ No newline at end of file
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 384c89f1..5090f3dd 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,12 +88,10 @@ class ContextSwitchesMetrics(pid: Long, log: LoggingAdapter, instrumentFactory:
object ContextSwitchesMetrics {
def register(system: ActorSystem, refreshInterval: FiniteDuration): ContextSwitchesMetrics = {
- val metricsExtension = Kamon.metrics
val log = Logging(system, "ContextSwitchesMetrics")
val pid = (new Sigar).getPid
- val instrumentFactory = metricsExtension.instrumentFactory("system-metric")
- metricsExtension.register(Entity("context-switches", "system-metric"), new ContextSwitchesMetrics(pid, log, instrumentFactory)).recorder
+ Kamon.metrics.entity(EntityRecorderFactory("system-metric", new ContextSwitchesMetrics(pid, log, _)), "context-switches")
}
}
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/jmx/GarbageCollectionMetrics.scala b/kamon-system-metrics/src/main/scala/kamon/system/jmx/GarbageCollectionMetrics.scala
index 59d4050c..7a5770d8 100644
--- a/kamon-system-metrics/src/main/scala/kamon/system/jmx/GarbageCollectionMetrics.scala
+++ b/kamon-system-metrics/src/main/scala/kamon/system/jmx/GarbageCollectionMetrics.scala
@@ -18,7 +18,8 @@ package kamon.system.jmx
import java.lang.management.{ GarbageCollectorMXBean, ManagementFactory }
-import kamon.metric.{ Entity, Metrics, GenericEntityRecorder }
+import kamon.Kamon
+import kamon.metric.{ EntityRecorderFactory, Entity, Metrics, GenericEntityRecorder }
import kamon.metric.instrument.{ DifferentialValueCollector, Time, InstrumentFactory }
import scala.collection.JavaConverters._
@@ -44,11 +45,9 @@ object GarbageCollectionMetrics {
name.replaceAll("""[^\w]""", "-").toLowerCase
def register(metricsExtension: Metrics): Unit = {
-
- val instrumentFactory = metricsExtension.instrumentFactory("system-metric")
ManagementFactory.getGarbageCollectorMXBeans.asScala.filter(_.isValid) map { gc ⇒
val gcName = sanitizeCollectorName(gc.getName)
- metricsExtension.register(Entity(s"$gcName-garbage-collector", "system-metric"), new GarbageCollectionMetrics(gc, instrumentFactory))
+ Kamon.metrics.entity(EntityRecorderFactory("system-metric", new GarbageCollectionMetrics(gc, _)), s"$gcName-garbage-collector")
}
}
}
diff --git a/kamon-system-metrics/src/main/scala/kamon/system/jmx/JmxSystemMetricRecorderCompanion.scala b/kamon-system-metrics/src/main/scala/kamon/system/jmx/JmxSystemMetricRecorderCompanion.scala
index 8837aec0..15bd399e 100644
--- a/kamon-system-metrics/src/main/scala/kamon/system/jmx/JmxSystemMetricRecorderCompanion.scala
+++ b/kamon-system-metrics/src/main/scala/kamon/system/jmx/JmxSystemMetricRecorderCompanion.scala
@@ -17,13 +17,11 @@
package kamon.system.jmx
import kamon.metric.instrument.InstrumentFactory
-import kamon.metric.{ Entity, EntityRecorder, Metrics }
+import kamon.metric.{ EntityRecorderFactory, EntityRecorder, Metrics }
abstract class JmxSystemMetricRecorderCompanion(metricName: String) {
- def register(metricsExtension: Metrics): EntityRecorder = {
- val instrumentFactory = metricsExtension.instrumentFactory("system-metric")
- metricsExtension.register(Entity(metricName, "system-metric"), apply(instrumentFactory)).recorder
- }
+ def register(metricsExtension: Metrics): EntityRecorder =
+ metricsExtension.entity(EntityRecorderFactory("system-metric", apply(_)), metricName)
def apply(instrumentFactory: InstrumentFactory): EntityRecorder
} \ No newline at end of file
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 68b133b0..a1bd3e01 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, Metrics }
+import kamon.metric.{ EntityRecorderFactory, Entity, EntityRecorder, Metrics }
import kamon.system.sigar.SigarMetricsUpdater.UpdateSigarMetrics
import org.hyperic.sigar.Sigar
@@ -65,10 +65,8 @@ trait SigarMetric extends EntityRecorder {
}
abstract class SigarMetricRecorderCompanion(metricName: String) {
- def register(sigar: Sigar, metricsExtension: Metrics): SigarMetric = {
- val instrumentFactory = metricsExtension.instrumentFactory("system-metric")
- metricsExtension.register(Entity(metricName, "system-metric"), apply(sigar, instrumentFactory)).recorder
- }
+ def register(sigar: Sigar, metricsExtension: Metrics): SigarMetric =
+ metricsExtension.entity(EntityRecorderFactory("system-metric", apply(sigar, _)), metricName)
def apply(sigar: Sigar, instrumentFactory: InstrumentFactory): SigarMetric
}
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index b9bd89e7..8d3e57fb 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -22,40 +22,40 @@ object Dependencies {
"typesafe repo" at "http://repo.typesafe.com/typesafe/releases/"
)
- val sprayVersion = "1.3.2"
- val akkaVersion = "2.3.9"
- val aspectjVersion = "1.8.4"
- val slf4jVersion = "1.7.7"
- val playVersion = "2.3.5"
- val sigarVersion = "1.6.5.132"
+ val sprayVersion = "1.3.2"
+ val akkaVersion = "2.3.9"
+ val aspectjVersion = "1.8.5"
+ val slf4jVersion = "1.7.7"
+ val playVersion = "2.3.8"
+ val sigarVersion = "1.6.5.132"
- val sprayJson = "io.spray" %% "spray-json" % "1.3.1"
- val sprayJsonLenses = "net.virtual-void" %% "json-lenses" % "0.6.0"
- val scalatest = "org.scalatest" %% "scalatest" % "2.2.1"
- val logback = "ch.qos.logback" % "logback-classic" % "1.0.13"
- val aspectJ = "org.aspectj" % "aspectjweaver" % aspectjVersion
- val newrelic = "com.newrelic.agent.java" % "newrelic-api" % "3.11.0"
- val hdrHistogram = "org.hdrhistogram" % "HdrHistogram" % "2.1.3"
- val sprayCan = "io.spray" %% "spray-can" % sprayVersion
- val sprayRouting = "io.spray" %% "spray-routing" % sprayVersion
- val sprayTestkit = "io.spray" %% "spray-testkit" % sprayVersion
- val sprayClient = "io.spray" %% "spray-client" % sprayVersion
- val akkaActor = "com.typesafe.akka" %% "akka-actor" % akkaVersion
- val akkaSlf4j = "com.typesafe.akka" %% "akka-slf4j" % akkaVersion
- val akkaTestKit = "com.typesafe.akka" %% "akka-testkit" % akkaVersion
- val akkaRemote = "com.typesafe.akka" %% "akka-remote" % akkaVersion
- val akkaCluster = "com.typesafe.akka" %% "akka-cluster" % akkaVersion
- val play = "com.typesafe.play" %% "play" % playVersion
- val playWS = "com.typesafe.play" %% "play-ws" % playVersion
- val playTest = "org.scalatestplus" %% "play" % "1.2.0"
- val slf4Api = "org.slf4j" % "slf4j-api" % slf4jVersion
- val slf4nop = "org.slf4j" % "slf4j-nop" % slf4jVersion
- val slf4Jul = "org.slf4j" % "jul-to-slf4j" % slf4jVersion
- val slf4Log4j = "org.slf4j" % "log4j-over-slf4j" % slf4jVersion
- val scalaCompiler = "org.scala-lang" % "scala-compiler" % Settings.ScalaVersion
- val scalazConcurrent = "org.scalaz" %% "scalaz-concurrent" % "7.1.0"
- val sigarLoader = "io.kamon" % "sigar-loader" % "1.6.5-rev001"
- val h2 = "com.h2database" % "h2" % "1.4.182"
+ val sprayJson = "io.spray" %% "spray-json" % "1.3.1"
+ val sprayJsonLenses = "net.virtual-void" %% "json-lenses" % "0.6.0"
+ val scalatest = "org.scalatest" %% "scalatest" % "2.2.1"
+ val logback = "ch.qos.logback" % "logback-classic" % "1.0.13"
+ val aspectJ = "org.aspectj" % "aspectjweaver" % aspectjVersion
+ val newrelic = "com.newrelic.agent.java" % "newrelic-api" % "3.11.0"
+ val hdrHistogram = "org.hdrhistogram" % "HdrHistogram" % "2.1.4"
+ val sprayCan = "io.spray" %% "spray-can" % sprayVersion
+ val sprayRouting = "io.spray" %% "spray-routing" % sprayVersion
+ val sprayTestkit = "io.spray" %% "spray-testkit" % sprayVersion
+ val sprayClient = "io.spray" %% "spray-client" % sprayVersion
+ val akkaActor = "com.typesafe.akka" %% "akka-actor" % akkaVersion
+ val akkaSlf4j = "com.typesafe.akka" %% "akka-slf4j" % akkaVersion
+ val akkaTestKit = "com.typesafe.akka" %% "akka-testkit" % akkaVersion
+ val akkaRemote = "com.typesafe.akka" %% "akka-remote" % akkaVersion
+ val akkaCluster = "com.typesafe.akka" %% "akka-cluster" % akkaVersion
+ val play = "com.typesafe.play" %% "play" % playVersion
+ val playWS = "com.typesafe.play" %% "play-ws" % playVersion
+ val playTest = "org.scalatestplus" %% "play" % "1.2.0"
+ val slf4Api = "org.slf4j" % "slf4j-api" % slf4jVersion
+ val slf4nop = "org.slf4j" % "slf4j-nop" % slf4jVersion
+ val slf4Jul = "org.slf4j" % "jul-to-slf4j" % slf4jVersion
+ val slf4Log4j = "org.slf4j" % "log4j-over-slf4j" % slf4jVersion
+ val scalazConcurrent = "org.scalaz" %% "scalaz-concurrent" % "7.1.0"
+ val sigarLoader = "io.kamon" % "sigar-loader" % "1.6.5-rev001"
+ val h2 = "com.h2database" % "h2" % "1.4.182"
+ val el = "org.glassfish" % "javax.el" % "3.0.0"
def compile (deps: ModuleID*): Seq[ModuleID] = deps map (_ % "compile")
def provided (deps: ModuleID*): Seq[ModuleID] = deps map (_ % "provided")
diff --git a/project/Projects.scala b/project/Projects.scala
index d52a7764..96cc239e 100644
--- a/project/Projects.scala
+++ b/project/Projects.scala
@@ -22,8 +22,8 @@ object Projects extends Build {
import Dependencies._
lazy val kamon = Project("kamon", file("."))
- .aggregate(kamonCore, kamonSpray, kamonNewrelic, kamonPlayground, kamonDashboard, kamonTestkit, kamonPlay, kamonStatsD,
- kamonDatadog, kamonSystemMetrics, kamonLogReporter, kamonAkkaRemote, kamonJdbc)
+ .aggregate(kamonCore, kamonScala, kamonAkka, kamonSpray, kamonNewrelic, kamonPlayground, kamonDashboard, kamonTestkit,
+ kamonPlay, kamonStatsD, kamonDatadog, kamonSystemMetrics, kamonLogReporter, kamonAkkaRemote, kamonJdbc, kamonAnnotation)
.settings(basicSettings: _*)
.settings(formatSettings: _*)
.settings(noPublishing: _*)
@@ -193,5 +193,16 @@ object Projects extends Build {
test(h2,scalatest, akkaTestKit, slf4Api) ++
provided(aspectJ))
+ lazy val kamonAnnotation = Project("kamon-annotation", file("kamon-annotation"))
+ .dependsOn(kamonCore % "compile->compile;test->test")
+ .settings(basicSettings: _*)
+ .settings(formatSettings: _*)
+ .settings(aspectJSettings: _*)
+ .settings(
+ libraryDependencies ++=
+ compile(el) ++
+ test(scalatest, akkaTestKit, slf4Api) ++
+ provided(aspectJ))
+
val noPublishing = Seq(publish := (), publishLocal := (), publishArtifact := false)
-}
+} \ No newline at end of file
diff --git a/project/Publish.scala b/project/Publish.scala
index 025e258e..ddb64d2d 100644
--- a/project/Publish.scala
+++ b/project/Publish.scala
@@ -19,7 +19,7 @@ import sbt.Keys._
object Publish {
lazy val settings = Seq(
- crossPaths := false,
+ crossPaths := true,
pomExtra := kamonPomExtra,
publishTo := kamonRepo,
organization := "io.kamon",
diff --git a/project/Settings.scala b/project/Settings.scala
index 3758fb15..675d99f2 100644
--- a/project/Settings.scala
+++ b/project/Settings.scala
@@ -27,19 +27,16 @@ object Settings {
val JavaVersion = "1.6"
val ScalaVersion = "2.11.5"
-
+
lazy val basicSettings = Seq(
- scalaVersion := ScalaVersion,
+ crossScalaVersions := Seq("2.10.5", "2.11.6"),
resolvers ++= Dependencies.resolutionRepos,
fork in run := true,
+ parallelExecution in Test := false,
testGrouping in Test := singleTestPerJvm((definedTests in Test).value, (javaOptions in Test).value),
- javacOptions in compile := Seq(
+ javacOptions := Seq(
"-Xlint:-options",
- "-source", JavaVersion, "-target", JavaVersion
- ),
- javacOptions in doc := Seq(
- "-source", JavaVersion
- ),
+ "-source", JavaVersion, "-target", JavaVersion),
scalacOptions := Seq(
"-encoding",
"utf8",
@@ -75,4 +72,4 @@ object Settings {
.setPreference(AlignParameters, false)
.setPreference(AlignSingleLineCaseStatements, true)
.setPreference(DoubleIndentClassDeclaration, true)
-} \ No newline at end of file
+}
diff --git a/project/plugins.sbt b/project/plugins.sbt
index abd5e2a8..0b0fb5e7 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -4,7 +4,7 @@ resolvers += "Kamon Releases" at "http://repo.kamon.io"
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.6.0")
-addSbtPlugin("com.typesafe.sbt" % "sbt-aspectj" % "0.10.0")
+addSbtPlugin("com.typesafe.sbt" % "sbt-aspectj" % "0.10.1")
addSbtPlugin("com.github.gseitz" % "sbt-release" % "0.8.2")