diff options
29 files changed, 703 insertions, 749 deletions
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/instrumentation/AspectJWeaverMissingWarning.scala b/kamon-core/src/main/scala/kamon/util/Latency.scala index 4dc5ff41..52e044f8 100644 --- a/kamon-core/src/main/scala/kamon/instrumentation/AspectJWeaverMissingWarning.scala +++ b/kamon-core/src/main/scala/kamon/util/Latency.scala @@ -14,20 +14,16 @@ * ========================================================================================= */ -package kamon.instrumentation +package kamon.util -import _root_.akka.event.EventStream -import org.aspectj.lang.ProceedingJoinPoint -import org.aspectj.lang.annotation.{ Around, Pointcut, Aspect } +import kamon.metric.instrument.Histogram -@Aspect -class AspectJWeaverMissingWarning { - - @Pointcut("execution(* kamon.metric.MetricsExtension.printInitializationMessage(..)) && args(eventStream, *)") - def printInitializationMessage(eventStream: EventStream): Unit = {} - - @Around("printInitializationMessage(eventStream)") - def aroundPrintInitializationMessage(pjp: ProceedingJoinPoint, eventStream: EventStream): Unit = { - pjp.proceed(Array[AnyRef](eventStream, Boolean.box(true))) +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) |