aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Martens <jmartens@zendesk.com>2017-04-05 16:33:08 -0700
committerJason Martens <jmartens@zendesk.com>2017-04-13 14:35:20 -0700
commiteafc9d454afb7f8fc601a83c75a267165137b6e0 (patch)
treeecfd21c9e899d1675366691559a16b9e1de7f84f
parentfbff9e865d08af1207ca16876ab0f11657f3e87a (diff)
downloadKamon-eafc9d454afb7f8fc601a83c75a267165137b6e0.tar.gz
Kamon-eafc9d454afb7f8fc601a83c75a267165137b6e0.tar.bz2
Kamon-eafc9d454afb7f8fc601a83c75a267165137b6e0.zip
+ MetricsModule: Add default metrics loaded from config
-rw-r--r--kamon-core/src/main/resources/reference.conf11
-rwxr-xr-xkamon-core/src/main/scala/kamon/metric/MetricsModule.scala42
-rw-r--r--kamon-core/src/test/resources/application.conf11
-rw-r--r--kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala31
4 files changed, 76 insertions, 19 deletions
diff --git a/kamon-core/src/main/resources/reference.conf b/kamon-core/src/main/resources/reference.conf
index 69e8c54f..f687bfb8 100644
--- a/kamon-core/src/main/resources/reference.conf
+++ b/kamon-core/src/main/resources/reference.conf
@@ -167,4 +167,15 @@ kamon {
# Just a place holder to ensure that the key is always available. Non-core Kamon modules should provide their
# settings in a module-info section.
}
+
+ # Add tags to all reported metrics. Can be useful to identify the source of metrics from a particluar JVM instance.
+ # Example:
+ #
+ # default-tags {
+ # host: ${?HOSTNAME}
+ # container-name: ${?CONTAINER_NAME}
+ # }
+ default-tags {
+
+ }
} \ No newline at end of file
diff --git a/kamon-core/src/main/scala/kamon/metric/MetricsModule.scala b/kamon-core/src/main/scala/kamon/metric/MetricsModule.scala
index 864b7a0b..7c85bb02 100755
--- a/kamon-core/src/main/scala/kamon/metric/MetricsModule.scala
+++ b/kamon-core/src/main/scala/kamon/metric/MetricsModule.scala
@@ -16,14 +16,17 @@
package kamon.metric
+import java.util.Map.Entry
+
import akka.actor._
-import com.typesafe.config.Config
+import com.typesafe.config.{Config, ConfigValue, ConfigValueType}
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.JavaConverters._
import scala.collection.concurrent.TrieMap
import scala.concurrent.duration.FiniteDuration
@@ -236,6 +239,21 @@ private[kamon] class MetricsModuleImpl(config: Config) extends MetricsModule {
@volatile var settings = MetricsSettings(config)
+ val defaultTags: Map[String, String] = if (config.hasPath("kamon.default-tags")) {
+ config.getConfig("kamon.default-tags").resolve().entrySet().asScala
+ .collect {
+ case e: Entry[String, ConfigValue] if e.getValue.valueType() == ConfigValueType.STRING =>
+ (e.getKey, e.getValue.unwrapped().asInstanceOf[String])
+ case e: Entry[String, ConfigValue] if e.getValue.valueType() == ConfigValueType.NUMBER =>
+ (e.getKey, e.getValue.unwrapped().asInstanceOf[Int].toString)
+ case e: Entry[String, ConfigValue] if e.getValue.valueType() == ConfigValueType.BOOLEAN =>
+ (e.getKey, e.getValue.unwrapped().asInstanceOf[Boolean].toString)
+ }.toMap
+ }
+ else {
+ Map.empty
+ }
+
def shouldTrack(entity: Entity): Boolean =
settings.entityFilters.get(entity.category).map {
filter ⇒ filter.accept(entity.name)
@@ -245,7 +263,7 @@ private[kamon] class MetricsModuleImpl(config: Config) extends MetricsModule {
def registerHistogram(name: String, tags: Map[String, String], unitOfMeasurement: Option[UnitOfMeasurement],
dynamicRange: Option[DynamicRange]): Histogram = {
- val histogramEntity = Entity(name, SingleInstrumentEntityRecorder.Histogram, tags)
+ val histogramEntity = Entity(name, SingleInstrumentEntityRecorder.Histogram, tags ++ defaultTags)
val recorder = _trackedEntities.atomicGetOrElseUpdate(histogramEntity, {
val factory = instrumentFactory(histogramEntity.category)
HistogramRecorder(
@@ -258,12 +276,12 @@ private[kamon] class MetricsModuleImpl(config: Config) extends MetricsModule {
}
def removeHistogram(name: String, tags: Map[String, String]): Boolean =
- _trackedEntities.remove(Entity(name, SingleInstrumentEntityRecorder.Histogram, tags)).isDefined
+ _trackedEntities.remove(Entity(name, SingleInstrumentEntityRecorder.Histogram, tags ++ defaultTags)).isDefined
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 minMaxCounterEntity = Entity(name, SingleInstrumentEntityRecorder.MinMaxCounter, tags ++ defaultTags)
val recorder = _trackedEntities.atomicGetOrElseUpdate(minMaxCounterEntity, {
val factory = instrumentFactory(minMaxCounterEntity.category)
MinMaxCounterRecorder(
@@ -276,13 +294,13 @@ private[kamon] class MetricsModuleImpl(config: Config) extends MetricsModule {
}
def removeMinMaxCounter(name: String, tags: Map[String, String]): Boolean =
- _trackedEntities.remove(Entity(name, SingleInstrumentEntityRecorder.MinMaxCounter, tags)).isDefined
+ _trackedEntities.remove(Entity(name, SingleInstrumentEntityRecorder.MinMaxCounter, tags ++ defaultTags)).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 gaugeEntity = Entity(name, SingleInstrumentEntityRecorder.Gauge, tags ++ defaultTags)
val recorder = _trackedEntities.atomicGetOrElseUpdate(gaugeEntity, {
val factory = instrumentFactory(gaugeEntity.category)
GaugeRecorder(
@@ -295,12 +313,12 @@ private[kamon] class MetricsModuleImpl(config: Config) extends MetricsModule {
}
def removeGauge(name: String, tags: Map[String, String]): Boolean =
- _trackedEntities.remove(Entity(name, SingleInstrumentEntityRecorder.Gauge, tags)).isDefined
+ _trackedEntities.remove(Entity(name, SingleInstrumentEntityRecorder.Gauge, tags ++ defaultTags)).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 counterEntity = Entity(name, SingleInstrumentEntityRecorder.Counter, tags ++ defaultTags)
val recorder = _trackedEntities.atomicGetOrElseUpdate(counterEntity, {
val factory = instrumentFactory(counterEntity.category)
CounterRecorder(
@@ -313,22 +331,22 @@ private[kamon] class MetricsModuleImpl(config: Config) extends MetricsModule {
}
def removeCounter(name: String, tags: Map[String, String]): Boolean =
- _trackedEntities.remove(Entity(name, SingleInstrumentEntityRecorder.Counter, tags)).isDefined
+ _trackedEntities.remove(Entity(name, SingleInstrumentEntityRecorder.Counter, tags ++ defaultTags)).isDefined
def entity[T <: EntityRecorder](recorderFactory: EntityRecorderFactory[T], entity: Entity): T = {
- _trackedEntities.atomicGetOrElseUpdate(entity, {
+ _trackedEntities.atomicGetOrElseUpdate(entity.copy(tags = entity.tags ++ defaultTags), {
recorderFactory.createRecorder(instrumentFactory(recorderFactory.category))
}, _.cleanup).asInstanceOf[T]
}
def removeEntity(entity: Entity): Boolean = {
- val removedEntity = _trackedEntities.remove(entity)
+ val removedEntity = _trackedEntities.remove(entity.copy(tags = entity.tags ++ defaultTags))
removedEntity.foreach(_.cleanup)
removedEntity.isDefined
}
def find(entity: Entity): Option[EntityRecorder] =
- _trackedEntities.get(entity)
+ _trackedEntities.get(entity.copy(tags = entity.tags ++ defaultTags))
def subscribe(filter: SubscriptionFilter, subscriber: ActorRef, permanent: Boolean): Unit =
_subscriptions.tell(Subscribe(filter, subscriber, permanent))
diff --git a/kamon-core/src/test/resources/application.conf b/kamon-core/src/test/resources/application.conf
index f79ab822..bf6123d9 100644
--- a/kamon-core/src/test/resources/application.conf
+++ b/kamon-core/src/test/resources/application.conf
@@ -12,4 +12,15 @@ kamon {
level-of-detail = simple-trace
sampling = all
}
+
+ default-tags = {
+ name = "jason"
+ number = 42
+ username = ${USER}
+ list = [1, 2, 3] // lists do not make sense for a tag
+ object = {
+ nested-bool = true
+ nested-string = "a string"
+ }
+ }
} \ No newline at end of file
diff --git a/kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala b/kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala
index 36cc62c3..ee34acc7 100644
--- a/kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala
+++ b/kamon-core/src/test/scala/kamon/metric/SubscriptionsProtocolSpec.scala
@@ -36,6 +36,7 @@ class SubscriptionsProtocolSpec extends BaseKamonSpec("subscriptions-protocol-sp
lazy val metricsModule = Kamon.metrics
import metricsModule.{entity, subscribe, unsubscribe}
+ val defaultTags: Map[String, String] = Kamon.metrics.defaultTags
"the Subscriptions messaging protocol" should {
"allow subscribing for a single tick" in {
@@ -47,12 +48,28 @@ class SubscriptionsProtocolSpec extends BaseKamonSpec("subscriptions-protocol-sp
val tickSnapshot = subscriber.expectMsgType[TickMetricSnapshot]
tickSnapshot.metrics.size should be(1)
- tickSnapshot.metrics.keys should contain(Entity("one-shot", "trace"))
+ tickSnapshot.metrics.keys should contain(Entity("one-shot", "trace", defaultTags))
flushSubscriptions()
subscriber.expectNoMsg(1 second)
}
+ "subscriptions should include default tags" in {
+ val subscriber = TestProbe()
+
+ Kamon.metrics.histogram("histogram-with-tags").record(1)
+ Kamon.metrics.subscribe("**", "**", subscriber.ref, permanently = true)
+ flushSubscriptions()
+
+ val tickSubscription = subscriber.expectMsgType[TickMetricSnapshot]
+ tickSubscription.metrics.head._1.tags.get("name") shouldBe Some("jason")
+ tickSubscription.metrics.head._1.tags.get("number") shouldBe Some("42")
+ tickSubscription.metrics.head._1.tags.get("username").isDefined shouldBe true
+ tickSubscription.metrics.head._1.tags.get("object.nested-bool") shouldBe Some("true")
+ tickSubscription.metrics.head._1.tags.get("object.nested-string") shouldBe Some("a string")
+ tickSubscription.metrics.head._1.tags.get("list") shouldBe None
+ }
+
"allow subscribing permanently to a metric" in {
val subscriber = TestProbe()
entity(TraceMetrics, "permanent")
@@ -63,7 +80,7 @@ class SubscriptionsProtocolSpec extends BaseKamonSpec("subscriptions-protocol-sp
val tickSnapshot = subscriber.expectMsgType[TickMetricSnapshot]
tickSnapshot.metrics.size should be(1)
- tickSnapshot.metrics.keys should contain(Entity("permanent", "trace"))
+ tickSnapshot.metrics.keys should contain(Entity("permanent", "trace", defaultTags))
}
}
@@ -79,8 +96,8 @@ class SubscriptionsProtocolSpec extends BaseKamonSpec("subscriptions-protocol-sp
val tickSnapshot = subscriber.expectMsgType[TickMetricSnapshot]
tickSnapshot.metrics.size should be(2)
- tickSnapshot.metrics.keys should contain(Entity("include-one", "trace"))
- tickSnapshot.metrics.keys should contain(Entity("include-three", "trace"))
+ tickSnapshot.metrics.keys should contain(Entity("include-one", "trace", defaultTags))
+ tickSnapshot.metrics.keys should contain(Entity("include-three", "trace", defaultTags))
}
}
@@ -97,8 +114,8 @@ class SubscriptionsProtocolSpec extends BaseKamonSpec("subscriptions-protocol-sp
val tickSnapshot = subscriber.expectMsgType[TickMetricSnapshot]
tickSnapshot.metrics.size should be(2)
- tickSnapshot.metrics.keys should contain(Entity("include-one", "trace"))
- tickSnapshot.metrics.keys should contain(Entity("include-three", "trace"))
+ tickSnapshot.metrics.keys should contain(Entity("include-one", "trace", defaultTags))
+ tickSnapshot.metrics.keys should contain(Entity("include-three", "trace", defaultTags))
}
}
@@ -110,7 +127,7 @@ class SubscriptionsProtocolSpec extends BaseKamonSpec("subscriptions-protocol-sp
flushSubscriptions()
val tickSnapshot = subscriber.expectMsgType[TickMetricSnapshot]
tickSnapshot.metrics.size should be(1)
- tickSnapshot.metrics.keys should contain(Entity("one-shot", "trace"))
+ tickSnapshot.metrics.keys should contain(Entity("one-shot", "trace", defaultTags))
unsubscribe(subscriber.ref)