aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.sbt21
-rw-r--r--kamon-core/src/main/resources/reference.conf54
-rw-r--r--kamon-core/src/main/scala/kamon/Diagnostic.scala13
-rw-r--r--kamon-core/src/main/scala/kamon/Environment.scala10
-rw-r--r--kamon-core/src/main/scala/kamon/Kamon.scala64
-rw-r--r--kamon-core/src/main/scala/kamon/Subscriptions.scala12
-rw-r--r--kamon-core/src/main/scala/kamon/Util.scala19
-rw-r--r--kamon-core/src/main/scala/kamon/metric/Entity.scala5
-rw-r--r--kamon-core/src/main/scala/kamon/metric/EntityRecorder.scala23
-rw-r--r--kamon-core/src/main/scala/kamon/metric/Metrics.scala34
-rw-r--r--kamon-core/src/main/scala/kamon/metric/MetricsSubscriber.scala12
-rw-r--r--kamon-core/src/main/scala/kamon/metric/instrument/Counter.scala11
-rw-r--r--kamon-core/src/main/scala/kamon/metric/instrument/DynamicRange.scala33
-rw-r--r--kamon-core/src/main/scala/kamon/metric/instrument/Gauge.scala15
-rw-r--r--kamon-core/src/main/scala/kamon/metric/instrument/Histogram.scala18
-rw-r--r--kamon-core/src/main/scala/kamon/metric/instrument/InstrumentFactory.scala102
-rw-r--r--kamon-core/src/main/scala/kamon/metric/instrument/MinMaxCounter.scala26
-rw-r--r--kamon-core/src/main/scala/kamon/metric/snapshot/Distribution.scala24
-rw-r--r--kamon-core/src/main/scala/kamon/metric/snapshot/EntitySnapshot.scala11
-rw-r--r--kamon-core/src/main/scala/kamon/metric/snapshot/InstrumentSnapshot.scala16
-rw-r--r--kamon-core/src/main/scala/kamon/metric/snapshot/SingleValue.scala5
-rw-r--r--kamon-core/src/main/scala/kamon/metric/snapshot/TickSnapshot.scala17
-rw-r--r--kamon-core/src/main/scala/kamon/package.scala49
-rw-r--r--kamon-core/src/main/scala/kamon/trace/Sampler.scala5
-rw-r--r--kamon-core/src/main/scala/kamon/trace/Tracer.scala5
-rw-r--r--kamon-core/src/main/scala/kamon/util/Clock.scala9
-rw-r--r--kamon-core/src/main/scala/kamon/util/EntityFilter.scala7
-rw-r--r--kamon-core/src/main/scala/kamon/util/MeasurementUnit.scala15
-rw-r--r--kamon-core/src/test/scala/kamon/metric/instrument/InstrumentFactorySpec.scala112
29 files changed, 738 insertions, 9 deletions
diff --git a/build.sbt b/build.sbt
index 60d4ad43..65fc7811 100644
--- a/build.sbt
+++ b/build.sbt
@@ -13,8 +13,6 @@
* =========================================================================================
*/
-parallelExecution in Test in Global := false
-
lazy val kamon = (project in file("."))
.settings(moduleName := "kamon")
.settings(noPublishing: _*)
@@ -24,19 +22,24 @@ lazy val kamon = (project in file("."))
lazy val core = (project in file("kamon-core"))
.settings(moduleName := "kamon-core")
.settings(
- libraryDependencies ++=
- compileScope(akkaDependency("actor").value, hdrHistogram, slf4jApi) ++
- providedScope(aspectJ) ++
- optionalScope(logbackClassic) ++
- testScope(scalatest, akkaDependency("testkit").value, akkaDependency("slf4j").value, logbackClassic))
+ scalaVersion := "2.12.1",
+ resolvers += Resolver.mavenLocal,
+ libraryDependencies ++= Seq(
+ "com.typesafe" % "config" % "1.3.1",
+ "org.slf4j" % "slf4j-api" % "1.7.7",
+ "org.hdrhistogram" % "HdrHistogram" % "2.1.9",
+ "io.opentracing" % "opentracing-api" % "0.21.1-SNAPSHOT",
+ "org.scalatest" %% "scalatest" % "3.0.1" % "test"
+ )
+)
lazy val testkit = (project in file("kamon-testkit"))
.dependsOn(core)
- .settings(moduleName := "kamon-testkit")
+ .settings(moduleName := "kamon-testkit", resolvers += Resolver.mavenLocal)
.settings(
libraryDependencies ++=
compileScope(akkaDependency("actor").value, akkaDependency("testkit").value) ++
providedScope(aspectJ) ++
- testScope(slf4jApi, slf4jnop))
+ testScope(slf4jApi, slf4jnop)) \ No newline at end of file
diff --git a/kamon-core/src/main/resources/reference.conf b/kamon-core/src/main/resources/reference.conf
new file mode 100644
index 00000000..9de2247b
--- /dev/null
+++ b/kamon-core/src/main/resources/reference.conf
@@ -0,0 +1,54 @@
+kamon {
+ metric {
+
+
+ instrument-factory {
+
+ # Default instrument settings for histograms and min max counters. The actual settings to be used when creating
+ # instruments is determined by merging the default settings, code settings and custom-settings using the following
+ # priorities (top wins):
+ #
+ # - any setting in the `custom-settings` section for the given category/instrument.
+ # - code settings provided when creating the instrument.
+ # - `default-settings` bellow.
+ #
+ default-settings {
+ histogram {
+ lowest-discernible-value = 0
+ highest-trackable-value = 3600000000000
+ significant-value-digits = 2
+ }
+
+ min-max-counter {
+ lowest-discernible-value = 0
+ highest-trackable-value = 3600000000000
+ significant-value-digits = 2
+ sample-interval = 200 millis
+ }
+ }
+
+ # Custom settings for instruments of a given category. The settings provided in this section override the default
+ # and manually provided settings when creating instruments. All settings are optional in this section and default
+ # values from the `kamon.metric.instrument-factory.default-settings` will be used in case of any setting being
+ # missing.
+ #
+ # Example:
+ # If you wish to change the highest trackable value setting of the `elapsed-time` instrument in the `span`
+ # category, you should include the following configuration in your application.conf file:
+ #
+ # kamon.metric.instrument-factory.custom-settings.span {
+ # elapsed-time {
+ # highest-trackable-value = 5000
+ # }
+ # }
+ #
+ # After including that configuration, every time a new histogram called `elapsed-time` for a entity with category
+ # `span`, the highest-trackable-value will be 5000, but lowest-discernible-value and significant-value-digits will
+ # remain with default values.
+ #
+ custom-settings {
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/kamon-core/src/main/scala/kamon/Diagnostic.scala b/kamon-core/src/main/scala/kamon/Diagnostic.scala
new file mode 100644
index 00000000..87784a72
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/Diagnostic.scala
@@ -0,0 +1,13 @@
+package kamon
+
+// The types are just an idea, they will need further refinement.
+trait Diagnostic {
+ def isAspectJWorking: Boolean
+ def detectedModules: Seq[String]
+ def entityFilterPatterns: Seq[String]
+ def metricSubscribers: Seq[String]
+ def traceSubscribers: Seq[String]
+
+ // Category Name => Count
+ def entityCount: Map[String, Long]
+}
diff --git a/kamon-core/src/main/scala/kamon/Environment.scala b/kamon-core/src/main/scala/kamon/Environment.scala
new file mode 100644
index 00000000..3184184a
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/Environment.scala
@@ -0,0 +1,10 @@
+package kamon
+
+import com.typesafe.config.Config
+
+trait Environment {
+ def instance: String
+ def host: String
+ def application: String
+ def config: Config
+}
diff --git a/kamon-core/src/main/scala/kamon/Kamon.scala b/kamon-core/src/main/scala/kamon/Kamon.scala
new file mode 100644
index 00000000..1dd72ab6
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/Kamon.scala
@@ -0,0 +1,64 @@
+package kamon
+
+import com.typesafe.config.Config
+import kamon.metric.Metrics
+import kamon.trace.Tracer
+
+/**
+ * The main entry point to all Kamon functionality.
+ *
+ *
+ *
+ *
+ */
+trait Kamon {
+ def metrics: Metrics
+ def tracer: Tracer
+
+ def subscriptions: Subscriptions
+ def util: Util
+
+ def environment: Environment
+ def diagnose: Diagnostic
+
+ def reconfigure(config: Config): Unit
+
+
+}
+
+
+
+/*
+
+Kamon.metrics.getRecorder("app-metrics")
+Kamon.metrics.getRecorder("akka-actor", "test")
+
+Kamon.entities.get("akka-actor", "test")
+Kamon.entities.remove(entity)
+
+Kamon.util.entityFilters.accept(entity)
+Kamon.util.clock.
+
+Kamon.entities.new().
+
+Kamon.subscriptions.loadFromConfig()
+Kamon.subscriptions.subscribe(StatsD, Filters.IncludeAll)
+Kamon.subscriptions.subscribe(NewRelic, Filters.Empty().includeCategory("span").withTag("span.kind", "server"))
+
+
+Things that you need to do with Kamon:
+Global:
+ - Reconfigure
+ - Get Diagnostic Data
+Metrics:
+ - create entities
+ - subscribe to metrics data
+
+Tracer:
+ - Build Spans / Use ActiveSpanSource
+ - subscribe to tracing data
+
+ */
+
+
+
diff --git a/kamon-core/src/main/scala/kamon/Subscriptions.scala b/kamon-core/src/main/scala/kamon/Subscriptions.scala
new file mode 100644
index 00000000..ff5dda4c
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/Subscriptions.scala
@@ -0,0 +1,12 @@
+package kamon
+
+import kamon.metric.MetricsSubscriber
+
+trait Subscriptions {
+ def loadFromConfig()
+ def subscribeToMetrics(subscriber: MetricsSubscriber): Subscription
+}
+
+trait Subscription {
+ def cancel(): Unit
+}
diff --git a/kamon-core/src/main/scala/kamon/Util.scala b/kamon-core/src/main/scala/kamon/Util.scala
new file mode 100644
index 00000000..51282afc
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/Util.scala
@@ -0,0 +1,19 @@
+package kamon
+
+import kamon.util.{Clock, EntityFilter}
+
+/**
+ * Useful classes for Kamon and submodules.
+ *
+ */
+trait Util {
+ /**
+ * @return The Clock instance used by Kamon for timestamps and latency measurements.
+ */
+ def clock: Clock
+
+ /**
+ * @return Currently configured entity filters.
+ */
+ def entityFilter: EntityFilter
+}
diff --git a/kamon-core/src/main/scala/kamon/metric/Entity.scala b/kamon-core/src/main/scala/kamon/metric/Entity.scala
new file mode 100644
index 00000000..5e2b8b46
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/metric/Entity.scala
@@ -0,0 +1,5 @@
+package kamon.metric
+
+
+
+case class Entity(name: String, category: String, tags: Map[String, String])
diff --git a/kamon-core/src/main/scala/kamon/metric/EntityRecorder.scala b/kamon-core/src/main/scala/kamon/metric/EntityRecorder.scala
new file mode 100644
index 00000000..a94881d2
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/metric/EntityRecorder.scala
@@ -0,0 +1,23 @@
+package kamon.metric
+
+import java.time.Duration
+
+import kamon.metric.instrument._
+import kamon.util.MeasurementUnit
+
+trait EntityRecorder {
+ def histogram(name: String): Histogram
+ def histogram(name: String, measurementUnit: MeasurementUnit, dynamicRange: DynamicRange): Histogram
+
+ def minMaxCounter(name: String): MinMaxCounter
+ def minMaxCounter(name: String, measurementUnit: MeasurementUnit, dynamicRange: DynamicRange, sampleFrequency: Duration): MinMaxCounter
+
+ def gauge(name: String): Gauge
+
+ def counter(name: String): Counter
+}
+
+
+class EntityRecorderImpl {
+
+} \ No newline at end of file
diff --git a/kamon-core/src/main/scala/kamon/metric/Metrics.scala b/kamon-core/src/main/scala/kamon/metric/Metrics.scala
new file mode 100644
index 00000000..f312c5b7
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/metric/Metrics.scala
@@ -0,0 +1,34 @@
+package kamon
+package metric
+
+import scala.collection.concurrent.TrieMap
+
+
+trait Metrics {
+ def getRecorder(entity: Entity): EntityRecorder
+ def getRecorder(name: String, category: String, tags: Map[String, String]): EntityRecorder
+
+ def removeRecorder(entity: Entity): Boolean
+ def removeRecorder(name: String, category: String, tags: Map[String, String]): Boolean
+}
+
+class MetricsImpl extends Metrics{
+ private val entities = TrieMap.empty[Entity, EntityRecorder]
+
+ override def getRecorder(entity: Entity): EntityRecorder = {
+ ???
+ }
+
+ override def getRecorder(name: String, category: String, tags: Map[String, String]): EntityRecorder = ???
+
+ override def removeRecorder(entity: Entity): Boolean = ???
+
+ override def removeRecorder(name: String, category: String, tags: Map[String, String]): Boolean = ???
+}
+
+
+
+
+
+
+
diff --git a/kamon-core/src/main/scala/kamon/metric/MetricsSubscriber.scala b/kamon-core/src/main/scala/kamon/metric/MetricsSubscriber.scala
new file mode 100644
index 00000000..dbdfde9d
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/metric/MetricsSubscriber.scala
@@ -0,0 +1,12 @@
+package kamon.metric
+
+import com.typesafe.config.Config
+
+trait MetricsSubscriber {
+ def reconfigure(config: Config): Unit
+
+ def start(config: Config): Unit
+ def shutdown(): Unit
+
+ def processTick(snapshot: String)
+}
diff --git a/kamon-core/src/main/scala/kamon/metric/instrument/Counter.scala b/kamon-core/src/main/scala/kamon/metric/instrument/Counter.scala
new file mode 100644
index 00000000..4a9edd77
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/metric/instrument/Counter.scala
@@ -0,0 +1,11 @@
+package kamon.metric.instrument
+
+import kamon.metric.Entity
+
+trait Counter {
+ def increment(): Unit
+}
+
+object Counter {
+ def apply(entity: Entity, name: String): Counter = ???
+}
diff --git a/kamon-core/src/main/scala/kamon/metric/instrument/DynamicRange.scala b/kamon-core/src/main/scala/kamon/metric/instrument/DynamicRange.scala
new file mode 100644
index 00000000..628439c2
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/metric/instrument/DynamicRange.scala
@@ -0,0 +1,33 @@
+package kamon.metric.instrument
+
+import java.util.concurrent.TimeUnit
+
+case class DynamicRange(lowestDiscernibleValue: Long, highestTrackableValue: Long, significantValueDigits: Int) {
+ def upTo(highestTrackableValue: Long): DynamicRange =
+ copy(highestTrackableValue = highestTrackableValue)
+
+ def startingFrom(lowestDiscernibleValue: Long): DynamicRange =
+ copy(lowestDiscernibleValue = lowestDiscernibleValue)
+}
+
+object DynamicRange {
+ private val oneHourInNanoseconds = TimeUnit.HOURS.toNanos(1)
+
+ /**
+ * Provides a range from 0 to 3.6e+12 (one hour in nanoseconds) with a value precision of 1 significant digit (10%)
+ * across that range.
+ */
+ val Loose = DynamicRange(0L, oneHourInNanoseconds, 1)
+
+ /**
+ * Provides a range from 0 to 3.6e+12 (one hour in nanoseconds) with a value precision of 2 significant digit (1%)
+ * across that range.
+ */
+ val Default = DynamicRange(0L, oneHourInNanoseconds, 2)
+
+ /**
+ * Provides a range from 0 to 3.6e+12 (one hour in nanoseconds) with a value precision of 3 significant digit (0.1%)
+ * across that range.
+ */
+ val Fine = DynamicRange(0L, oneHourInNanoseconds, 3)
+}
diff --git a/kamon-core/src/main/scala/kamon/metric/instrument/Gauge.scala b/kamon-core/src/main/scala/kamon/metric/instrument/Gauge.scala
new file mode 100644
index 00000000..43c71206
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/metric/instrument/Gauge.scala
@@ -0,0 +1,15 @@
+package kamon.metric.instrument
+
+import kamon.metric.Entity
+
+trait Gauge {
+ def increment(): Unit
+ def increment(times: Long): Unit
+ def decrement(): Unit
+ def decrement(times: Long): Unit
+ def set(value: Long): Unit
+}
+
+object Gauge {
+ def apply(entity: Entity, name: String): Gauge = ???
+}
diff --git a/kamon-core/src/main/scala/kamon/metric/instrument/Histogram.scala b/kamon-core/src/main/scala/kamon/metric/instrument/Histogram.scala
new file mode 100644
index 00000000..c43c9dbc
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/metric/instrument/Histogram.scala
@@ -0,0 +1,18 @@
+package kamon.metric.instrument
+
+import kamon.metric.Entity
+
+trait Histogram {
+ def dynamicRange: DynamicRange
+
+ def record(value: Long): Unit
+ def record(value: Long, times: Long): Unit
+}
+
+object Histogram {
+ def apply(entity: Entity, name: String, dynamicRange2: DynamicRange): Histogram = new Histogram {
+ override def dynamicRange: DynamicRange = dynamicRange2
+ override def record(value: Long): Unit = ???
+ override def record(value: Long, times: Long): Unit = ???
+ }
+}
diff --git a/kamon-core/src/main/scala/kamon/metric/instrument/InstrumentFactory.scala b/kamon-core/src/main/scala/kamon/metric/instrument/InstrumentFactory.scala
new file mode 100644
index 00000000..820c05b5
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/metric/instrument/InstrumentFactory.scala
@@ -0,0 +1,102 @@
+package kamon
+package metric
+package instrument
+
+import java.time.Duration
+
+import com.typesafe.config.Config
+import kamon.metric.instrument.InstrumentFactory.CustomInstrumentSettings
+
+
+private[kamon] class InstrumentFactory private (
+ defaultHistogramDynamicRange: DynamicRange,
+ defaultMMCounterDynamicRange: DynamicRange,
+ defaultMMCounterSampleRate: Duration,
+ customSettings: Map[(String, String), CustomInstrumentSettings]) {
+
+ def buildHistogram(entity: Entity, name: String, dynamicRange: DynamicRange = defaultHistogramDynamicRange): Histogram =
+ Histogram(entity, name, instrumentDynamicRange(entity, name, dynamicRange))
+
+ def buildMinMaxCounter(entity: Entity, name: String, dynamicRange: DynamicRange = defaultMMCounterDynamicRange,
+ sampleInterval: Duration = defaultMMCounterSampleRate): MinMaxCounter =
+ MinMaxCounter(
+ entity,
+ name,
+ instrumentDynamicRange(entity, name, dynamicRange),
+ instrumentSampleInterval(entity, name, sampleInterval)
+ )
+
+ def buildGauge(entity: Entity, name: String): Gauge =
+ Gauge(entity, name)
+
+ def buildCounter(entity: Entity, name: String): Counter =
+ Counter(entity, name)
+
+
+ private def instrumentDynamicRange(entity: Entity, instrumentName: String, dynamicRange: DynamicRange): DynamicRange =
+ customSettings.get((entity.category, instrumentName)).fold(dynamicRange) { cs =>
+ overrideDynamicRange(dynamicRange, cs)
+ }
+
+ private def instrumentSampleInterval(entity: Entity, instrumentName: String, sampleInterval: Duration): Duration =
+ customSettings.get((entity.category, instrumentName)).fold(sampleInterval) { cs =>
+ cs.sampleInterval.getOrElse(sampleInterval)
+ }
+
+ private def overrideDynamicRange(defaultDynamicRange: DynamicRange, customSettings: CustomInstrumentSettings): DynamicRange =
+ DynamicRange(
+ customSettings.lowestDiscernibleValue.getOrElse(defaultDynamicRange.lowestDiscernibleValue),
+ customSettings.highestTrackableValue.getOrElse(defaultDynamicRange.highestTrackableValue),
+ customSettings.significantValueDigits.getOrElse(defaultDynamicRange.significantValueDigits)
+ )
+}
+
+object InstrumentFactory {
+
+ def apply(config: Config): InstrumentFactory = {
+ val histogramDynamicRange = readDynamicRange(config.getConfig("default-settings.histogram"))
+ val mmCounterDynamicRange = readDynamicRange(config.getConfig("default-settings.min-max-counter"))
+ val mmCounterSampleInterval = config.getDuration("default-settings.min-max-counter.sample-interval")
+
+ val customSettings = config.getConfig("custom-settings")
+ .configurations
+ .filter(nonEmptyCategories)
+ .flatMap(buildCustomInstrumentSettings)
+
+ new InstrumentFactory(histogramDynamicRange, mmCounterDynamicRange, mmCounterSampleInterval, customSettings)
+ }
+
+ private def nonEmptyCategories(entry: (String, Config)): Boolean = entry match {
+ case (_, config) => config.firstLevelKeys.nonEmpty
+ }
+
+ private def buildCustomInstrumentSettings(entry: (String, Config)): Map[(String, String), CustomInstrumentSettings] = {
+ val (category, categoryConfig) = entry
+ categoryConfig.configurations.map {
+ case (instrumentName, instrumentConfig) => (category, instrumentName) -> readCustomSettings(instrumentConfig)
+ }
+ }
+
+ private def readDynamicRange(config: Config): DynamicRange =
+ DynamicRange(
+ lowestDiscernibleValue = config.getLong("lowest-discernible-value"),
+ highestTrackableValue = config.getLong("highest-trackable-value"),
+ significantValueDigits = config.getInt("significant-value-digits")
+ )
+
+
+ private case class CustomInstrumentSettings(
+ lowestDiscernibleValue: Option[Long],
+ highestTrackableValue: Option[Long],
+ significantValueDigits: Option[Int],
+ sampleInterval: Option[Duration]
+ )
+
+ private def readCustomSettings(config: Config): CustomInstrumentSettings =
+ CustomInstrumentSettings(
+ if (config.hasPath("lowest-discernible-value")) Some(config.getLong("lowest-discernible-value")) else None,
+ if (config.hasPath("highest-trackable-value")) Some(config.getLong("highest-trackable-value")) else None,
+ if (config.hasPath("significant-value-digits")) Some(config.getInt("significant-value-digits")) else None,
+ if (config.hasPath("sample-interval")) Some(config.getDuration("sample-interval")) else None
+ )
+} \ No newline at end of file
diff --git a/kamon-core/src/main/scala/kamon/metric/instrument/MinMaxCounter.scala b/kamon-core/src/main/scala/kamon/metric/instrument/MinMaxCounter.scala
new file mode 100644
index 00000000..34a983a9
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/metric/instrument/MinMaxCounter.scala
@@ -0,0 +1,26 @@
+package kamon.metric.instrument
+
+import java.time.Duration
+
+import kamon.metric.Entity
+
+trait MinMaxCounter {
+ def dynamicRange: DynamicRange
+ def sampleInterval: Duration
+
+ def increment(): Unit
+ def increment(times: Long): Unit
+ def decrement(): Unit
+ def decrement(times: Long): Unit
+}
+
+object MinMaxCounter {
+ def apply(entity: Entity, name: String, dynamicRange2: DynamicRange, sampleInterval2: Duration): MinMaxCounter = new MinMaxCounter {
+ override def sampleInterval: Duration = sampleInterval2
+ override def increment(): Unit = ???
+ override def increment(times: Long): Unit = ???
+ override def decrement(): Unit = ???
+ override def decrement(times: Long): Unit = ???
+ override def dynamicRange: DynamicRange = dynamicRange2
+ }
+}
diff --git a/kamon-core/src/main/scala/kamon/metric/snapshot/Distribution.scala b/kamon-core/src/main/scala/kamon/metric/snapshot/Distribution.scala
new file mode 100644
index 00000000..99e6b24e
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/metric/snapshot/Distribution.scala
@@ -0,0 +1,24 @@
+package kamon.metric.snapshot
+
+trait Distribution {
+ def buckets: Seq[Bucket]
+ def bucketsIterator: Iterator[Bucket]
+
+ def min: Long
+ def max: Long
+ def sum: Long
+ def percentile(p: Double): Percentile
+
+ def percentiles: Seq[Percentile]
+ def percentilesIterator: Iterator[Percentile]
+}
+
+trait Bucket {
+ def value: Long
+ def frequency: Long
+}
+
+trait Percentile {
+ def value: Long
+ def count: Long
+}
diff --git a/kamon-core/src/main/scala/kamon/metric/snapshot/EntitySnapshot.scala b/kamon-core/src/main/scala/kamon/metric/snapshot/EntitySnapshot.scala
new file mode 100644
index 00000000..a2b7b606
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/metric/snapshot/EntitySnapshot.scala
@@ -0,0 +1,11 @@
+package kamon.metric.snapshot
+
+import kamon.metric.Entity
+
+trait EntitySnapshot {
+ def entity: Entity
+ def histograms: Seq[DistributionSnapshot]
+ def minMaxCounters: Seq[DistributionSnapshot]
+ def gauges: Seq[SingleValueSnapshot]
+ def counters: Seq[SingleValueSnapshot]
+} \ No newline at end of file
diff --git a/kamon-core/src/main/scala/kamon/metric/snapshot/InstrumentSnapshot.scala b/kamon-core/src/main/scala/kamon/metric/snapshot/InstrumentSnapshot.scala
new file mode 100644
index 00000000..624121c0
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/metric/snapshot/InstrumentSnapshot.scala
@@ -0,0 +1,16 @@
+package kamon.metric.snapshot
+
+import kamon.util.MeasurementUnit
+
+sealed trait InstrumentSnapshot {
+ def name: String
+ def measurementUnit: MeasurementUnit
+}
+
+trait DistributionSnapshot extends InstrumentSnapshot {
+ def distribution: Distribution
+}
+
+trait SingleValueSnapshot extends InstrumentSnapshot {
+ def value: Long
+} \ No newline at end of file
diff --git a/kamon-core/src/main/scala/kamon/metric/snapshot/SingleValue.scala b/kamon-core/src/main/scala/kamon/metric/snapshot/SingleValue.scala
new file mode 100644
index 00000000..ac67620c
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/metric/snapshot/SingleValue.scala
@@ -0,0 +1,5 @@
+package kamon.metric.snapshot
+
+trait SingleValue {
+ def value: Long
+}
diff --git a/kamon-core/src/main/scala/kamon/metric/snapshot/TickSnapshot.scala b/kamon-core/src/main/scala/kamon/metric/snapshot/TickSnapshot.scala
new file mode 100644
index 00000000..a1bdd6b1
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/metric/snapshot/TickSnapshot.scala
@@ -0,0 +1,17 @@
+package kamon.metric.snapshot
+
+import java.time.Instant
+
+
+trait TickSnapshot {
+ def interval: Interval
+ def entities: Seq[EntitySnapshot]
+}
+
+trait Interval {
+ def from: Instant
+ def to: Instant
+}
+
+
+
diff --git a/kamon-core/src/main/scala/kamon/package.scala b/kamon-core/src/main/scala/kamon/package.scala
new file mode 100644
index 00000000..3d8c3515
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/package.scala
@@ -0,0 +1,49 @@
+import com.typesafe.config.Config
+
+import scala.collection.concurrent.TrieMap
+
+package object kamon {
+
+
+
+ /**
+ * Workaround to the non thread-safe [[scala.collection.concurrent.TrieMap#getOrElseUpdate]] method. More details on
+ * why this is necessary can be found at [[https://issues.scala-lang.org/browse/SI-7943]].
+ */
+ implicit class AtomicGetOrElseUpdateOnTrieMap[K, V](val trieMap: TrieMap[K, V]) extends AnyVal {
+
+ def atomicGetOrElseUpdate(key: K, op: ⇒ V): V =
+ atomicGetOrElseUpdate(key, op, { v: V ⇒ Unit })
+
+ def atomicGetOrElseUpdate(key: K, op: ⇒ V, cleanup: V ⇒ Unit): V =
+ trieMap.get(key) match {
+ case Some(v) ⇒ v
+ case None ⇒
+ val d = op
+ trieMap.putIfAbsent(key, d).map { oldValue ⇒
+ // If there was an old value then `d` was never added
+ // and thus need to be cleanup.
+ cleanup(d)
+ oldValue
+
+ } getOrElse (d)
+ }
+ }
+
+
+ implicit class UtilsOnConfig(val config: Config) extends AnyVal {
+ import scala.collection.JavaConverters._
+
+ def firstLevelKeys: Set[String] = {
+ config.entrySet().asScala.map {
+ case entry ⇒ entry.getKey.takeWhile(_ != '.')
+ } toSet
+ }
+
+ def configurations: Map[String, Config] = {
+ firstLevelKeys
+ .map(entry => (entry, config.getConfig(entry)))
+ .toMap
+ }
+ }
+}
diff --git a/kamon-core/src/main/scala/kamon/trace/Sampler.scala b/kamon-core/src/main/scala/kamon/trace/Sampler.scala
new file mode 100644
index 00000000..a26e61be
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/trace/Sampler.scala
@@ -0,0 +1,5 @@
+package kamon.trace
+
+trait Sampler {
+
+}
diff --git a/kamon-core/src/main/scala/kamon/trace/Tracer.scala b/kamon-core/src/main/scala/kamon/trace/Tracer.scala
new file mode 100644
index 00000000..802d95ec
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/trace/Tracer.scala
@@ -0,0 +1,5 @@
+package kamon.trace
+
+trait Tracer extends io.opentracing.Tracer {
+ def sampler: Sampler
+}
diff --git a/kamon-core/src/main/scala/kamon/util/Clock.scala b/kamon-core/src/main/scala/kamon/util/Clock.scala
new file mode 100644
index 00000000..55bb529a
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/util/Clock.scala
@@ -0,0 +1,9 @@
+package kamon.util
+
+trait Clock {
+ def nanoTimestamp(): Long
+ def microTimestamp(): Long
+ def milliTimestamp(): Long
+
+ def relativeNanoTimestamp(): Long
+}
diff --git a/kamon-core/src/main/scala/kamon/util/EntityFilter.scala b/kamon-core/src/main/scala/kamon/util/EntityFilter.scala
new file mode 100644
index 00000000..a8456689
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/util/EntityFilter.scala
@@ -0,0 +1,7 @@
+package kamon.util
+
+import kamon.metric.Entity
+
+trait EntityFilter {
+ def accept(entity: Entity): Boolean
+}
diff --git a/kamon-core/src/main/scala/kamon/util/MeasurementUnit.scala b/kamon-core/src/main/scala/kamon/util/MeasurementUnit.scala
new file mode 100644
index 00000000..4130fa50
--- /dev/null
+++ b/kamon-core/src/main/scala/kamon/util/MeasurementUnit.scala
@@ -0,0 +1,15 @@
+package kamon.util
+
+trait MeasurementUnit {
+ def dimension: Dimension
+ def magnitude: Magnitude
+}
+
+trait Magnitude {
+ def name: String
+}
+
+trait Dimension {
+ def name: String
+ def scale(value: Long, from: Magnitude, to: Magnitude): Double
+}
diff --git a/kamon-core/src/test/scala/kamon/metric/instrument/InstrumentFactorySpec.scala b/kamon-core/src/test/scala/kamon/metric/instrument/InstrumentFactorySpec.scala
new file mode 100644
index 00000000..fc82ddcd
--- /dev/null
+++ b/kamon-core/src/test/scala/kamon/metric/instrument/InstrumentFactorySpec.scala
@@ -0,0 +1,112 @@
+package kamon.metric.instrument
+
+import java.time.Duration
+
+import com.typesafe.config.ConfigFactory
+import kamon.metric.Entity
+import org.scalatest.{Matchers, WordSpec}
+
+class InstrumentFactorySpec extends WordSpec with Matchers{
+ val testEntity = Entity("test", "test-category", Map.empty)
+ val customEntity = Entity("test", "custom-category", Map.empty)
+ val baseConfiguration = ConfigFactory.parseString(
+ """
+ |default-settings {
+ | histogram {
+ | lowest-discernible-value = 100
+ | highest-trackable-value = 5000
+ | significant-value-digits = 2
+ | }
+ |
+ | min-max-counter {
+ | lowest-discernible-value = 200
+ | highest-trackable-value = 6000
+ | significant-value-digits = 3
+ | sample-interval = 647 millis
+ | }
+ |}
+ |
+ |custom-settings {
+ |
+ |}
+ """.stripMargin
+ )
+
+
+ "the metrics InstrumentFactory" should {
+ "create instruments using the default configuration settings" in {
+ val factory = InstrumentFactory(baseConfiguration)
+ val histogram = factory.buildHistogram(testEntity, "my-histogram")
+ val mmCounter = factory.buildMinMaxCounter(testEntity, "my-mm-counter")
+
+ histogram.dynamicRange.lowestDiscernibleValue shouldBe(100)
+ histogram.dynamicRange.highestTrackableValue shouldBe(5000)
+ histogram.dynamicRange.significantValueDigits shouldBe(2)
+
+ mmCounter.dynamicRange.lowestDiscernibleValue shouldBe(200)
+ mmCounter.dynamicRange.highestTrackableValue shouldBe(6000)
+ mmCounter.dynamicRange.significantValueDigits shouldBe(3)
+ mmCounter.sampleInterval shouldBe(Duration.ofMillis(647))
+ }
+
+ "accept custom settings when building instruments" in {
+ val factory = InstrumentFactory(baseConfiguration)
+ val histogram = factory.buildHistogram(testEntity, "my-histogram", DynamicRange.Loose)
+ val mmCounter = factory.buildMinMaxCounter(testEntity, "my-mm-counter", DynamicRange.Fine, Duration.ofMillis(500))
+
+ histogram.dynamicRange shouldBe(DynamicRange.Loose)
+
+ mmCounter.dynamicRange shouldBe(DynamicRange.Fine)
+ mmCounter.sampleInterval shouldBe(Duration.ofMillis(500))
+ }
+
+ "allow overriding any default and provided settings via the custom-settings configuration key" in {
+ val customConfig = ConfigFactory.parseString(
+ """
+ |custom-settings {
+ | custom-category {
+ | modified-histogram {
+ | lowest-discernible-value = 99
+ | highest-trackable-value = 999
+ | significant-value-digits = 7
+ | }
+ |
+ | modified-mm-counter {
+ | lowest-discernible-value = 784
+ | highest-trackable-value = 14785
+ | significant-value-digits = 1
+ | sample-interval = 3 seconds
+ | }
+ | }
+ |}
+ """.stripMargin
+ ).withFallback(baseConfiguration)
+
+ val factory = InstrumentFactory(customConfig)
+ val defaultHistogram = factory.buildHistogram(customEntity, "default-histogram")
+ val modifiedHistogram = factory.buildHistogram(customEntity, "modified-histogram", DynamicRange.Loose)
+
+ defaultHistogram.dynamicRange.lowestDiscernibleValue shouldBe(100)
+ defaultHistogram.dynamicRange.highestTrackableValue shouldBe(5000)
+ defaultHistogram.dynamicRange.significantValueDigits shouldBe(2)
+
+ modifiedHistogram.dynamicRange.lowestDiscernibleValue shouldBe(99)
+ modifiedHistogram.dynamicRange.highestTrackableValue shouldBe(999)
+ modifiedHistogram.dynamicRange.significantValueDigits shouldBe(7)
+
+
+ val defaultMMCounter = factory.buildMinMaxCounter(customEntity, "default-mm-counter")
+ val modifiedMMCounter = factory.buildMinMaxCounter(customEntity, "modified-mm-counter", DynamicRange.Loose)
+
+ defaultMMCounter.dynamicRange.lowestDiscernibleValue shouldBe(200)
+ defaultMMCounter.dynamicRange.highestTrackableValue shouldBe(6000)
+ defaultMMCounter.dynamicRange.significantValueDigits shouldBe(3)
+ defaultMMCounter.sampleInterval shouldBe(Duration.ofMillis(647))
+
+ modifiedMMCounter.dynamicRange.lowestDiscernibleValue shouldBe(784)
+ modifiedMMCounter.dynamicRange.highestTrackableValue shouldBe(14785)
+ modifiedMMCounter.dynamicRange.significantValueDigits shouldBe(1)
+ modifiedMMCounter.sampleInterval shouldBe(Duration.ofSeconds(3))
+ }
+ }
+}