From a90d4aa75e7fdf12a85177f4e81463439bfe5bb3 Mon Sep 17 00:00:00 2001 From: Ivan Topolnjak Date: Tue, 15 Aug 2017 00:33:06 +0200 Subject: separate the build into core, testkit and core-tests projects --- .../scala/kamon/testkit/MetricInspection.scala | 45 ++++++++++++++++ .../src/main/scala/kamon/testkit/Reconfigure.scala | 26 +++++++++ .../main/scala/kamon/testkit/SpanBuilding.scala | 16 ++++++ .../main/scala/kamon/testkit/SpanInspector.scala | 61 ++++++++++++++++++++++ .../scala/kamon/testkit/TestSpanReporter.scala | 23 ++++++++ 5 files changed, 171 insertions(+) create mode 100644 kamon-testkit/src/main/scala/kamon/testkit/MetricInspection.scala create mode 100644 kamon-testkit/src/main/scala/kamon/testkit/Reconfigure.scala create mode 100644 kamon-testkit/src/main/scala/kamon/testkit/SpanBuilding.scala create mode 100644 kamon-testkit/src/main/scala/kamon/testkit/SpanInspector.scala create mode 100644 kamon-testkit/src/main/scala/kamon/testkit/TestSpanReporter.scala (limited to 'kamon-testkit/src/main/scala') diff --git a/kamon-testkit/src/main/scala/kamon/testkit/MetricInspection.scala b/kamon-testkit/src/main/scala/kamon/testkit/MetricInspection.scala new file mode 100644 index 00000000..d0681fb5 --- /dev/null +++ b/kamon-testkit/src/main/scala/kamon/testkit/MetricInspection.scala @@ -0,0 +1,45 @@ +package kamon.testkit + +import kamon.metric._ +import _root_.scala.collection.concurrent.TrieMap + + +trait MetricInspection { + + implicit class MetricSyntax(metric: Metric[_]) { + def valuesForTag(tag: String): Seq[String] = { + val instrumentsField = classOf[BaseMetric[_, _]].getDeclaredField("instruments") + instrumentsField.setAccessible(true) + + val instruments = instrumentsField.get(metric).asInstanceOf[TrieMap[Map[String, String], _]] + val instrumentsWithTheTag = instruments.keys.filter(_.keys.find(_ == tag).nonEmpty) + instrumentsWithTheTag.map(t => t(tag)).toSeq + } + } + + implicit class HistogramMetricSyntax(histogram: Histogram) { + def distribution(resetState: Boolean = true): Distribution = + histogram match { + case hm: HistogramMetric => hm.refine(Map.empty[String, String]).distribution(resetState) + case h: AtomicHdrHistogram => h.snapshot(resetState).distribution + case h: HdrHistogram => h.snapshot(resetState).distribution + } + } + + implicit class MinMaxCounterMetricSyntax(mmCounter: MinMaxCounter) { + def distribution(resetState: Boolean = true): Distribution = + mmCounter match { + case mmcm: MinMaxCounterMetric => mmcm.refine(Map.empty[String, String]).distribution(resetState) + case mmc: SimpleMinMaxCounter => mmc.snapshot(resetState).distribution + } + } + + implicit class CounterMetricSyntax(counter: Counter) { + def value(resetState: Boolean = true): Long = + counter match { + case cm: CounterMetric => cm.refine(Map.empty[String, String]).value(resetState) + case c: LongAdderCounter => c.snapshot(resetState).value + } + } +} + diff --git a/kamon-testkit/src/main/scala/kamon/testkit/Reconfigure.scala b/kamon-testkit/src/main/scala/kamon/testkit/Reconfigure.scala new file mode 100644 index 00000000..4b3b2cdb --- /dev/null +++ b/kamon-testkit/src/main/scala/kamon/testkit/Reconfigure.scala @@ -0,0 +1,26 @@ +package kamon.testkit + +import com.typesafe.config.ConfigFactory +import kamon.Kamon + +trait Reconfigure { + + def enableFastSpanFlushing(): Unit = { + applyConfig("kamon.trace.tick-interval = 1 millisecond") + } + + def sampleAlways(): Unit = { + applyConfig("kamon.trace.sampler = always") + } + + def sampleNever(): Unit = { + applyConfig("kamon.trace.sampler = never") + } + + private def applyConfig(configString: String): Unit = { + Kamon.reconfigure(ConfigFactory.parseString(configString).withFallback(Kamon.config())) + } + + + +} diff --git a/kamon-testkit/src/main/scala/kamon/testkit/SpanBuilding.scala b/kamon-testkit/src/main/scala/kamon/testkit/SpanBuilding.scala new file mode 100644 index 00000000..7a216ecc --- /dev/null +++ b/kamon-testkit/src/main/scala/kamon/testkit/SpanBuilding.scala @@ -0,0 +1,16 @@ +package kamon.testkit + +import kamon.trace.SpanContext.SamplingDecision +import kamon.trace.{IdentityProvider, SpanContext} + +trait SpanBuilding { + private val identityProvider = IdentityProvider.Default() + + def createSpanContext(samplingDecision: SamplingDecision = SamplingDecision.Sample): SpanContext = + SpanContext( + traceID = identityProvider.traceIdGenerator().generate(), + spanID = identityProvider.spanIdGenerator().generate(), + parentID = identityProvider.spanIdGenerator().generate(), + samplingDecision = samplingDecision + ) +} diff --git a/kamon-testkit/src/main/scala/kamon/testkit/SpanInspector.scala b/kamon-testkit/src/main/scala/kamon/testkit/SpanInspector.scala new file mode 100644 index 00000000..f23fba98 --- /dev/null +++ b/kamon-testkit/src/main/scala/kamon/testkit/SpanInspector.scala @@ -0,0 +1,61 @@ +package kamon.testkit + +import kamon.trace.{Span, SpanContext} +import kamon.trace.Span.FinishedSpan +import kamon.util.Clock + +import scala.reflect.ClassTag +import scala.util.Try + +class SpanInspector(span: Span) { + private val (realSpan, spanData) = Try { + val realSpan = span match { + case _: Span.Local => span + } + + val spanData = invoke[Span.Local, FinishedSpan](realSpan, "toFinishedSpan", classOf[Long] -> Long.box(Clock.microTimestamp())) + (realSpan, spanData) + }.getOrElse((null, null)) + + def isEmpty: Boolean = + realSpan == null + + def spanTag(key: String): Option[Span.TagValue] = + spanData.tags.get(key) + + def spanTags(): Map[String, Span.TagValue] = + spanData.tags + + def metricTags(): Map[String, String] = + getField[Span.Local, Map[String, String]](realSpan, "customMetricTags") + + def startTimestamp(): Long = + getField[Span.Local, Long](realSpan, "startTimestampMicros") + + def context(): SpanContext = + spanData.context + + def operationName(): String = + spanData.operationName + + + + + private def getField[T, R](target: Any, fieldName: String)(implicit classTag: ClassTag[T]): R = { + val toFinishedSpanMethod = classTag.runtimeClass.getDeclaredField(fieldName) + toFinishedSpanMethod.setAccessible(true) + toFinishedSpanMethod.get(target).asInstanceOf[R] + } + + private def invoke[T, R](target: Any, fieldName: String, parameters: (Class[_], AnyRef)*)(implicit classTag: ClassTag[T]): R = { + val parameterClasses = parameters.map(_._1) + val parameterInstances = parameters.map(_._2) + val toFinishedSpanMethod = classTag.runtimeClass.getDeclaredMethod(fieldName, parameterClasses: _*) + toFinishedSpanMethod.setAccessible(true) + toFinishedSpanMethod.invoke(target, parameterInstances: _*).asInstanceOf[R] + } +} + +object SpanInspector { + def apply(span: Span): SpanInspector = new SpanInspector(span) +} diff --git a/kamon-testkit/src/main/scala/kamon/testkit/TestSpanReporter.scala b/kamon-testkit/src/main/scala/kamon/testkit/TestSpanReporter.scala new file mode 100644 index 00000000..8ea2d433 --- /dev/null +++ b/kamon-testkit/src/main/scala/kamon/testkit/TestSpanReporter.scala @@ -0,0 +1,23 @@ +package kamon.testkit + +import java.util.concurrent.LinkedBlockingQueue + +import com.typesafe.config.Config +import kamon.SpanReporter +import kamon.trace.Span +import kamon.trace.Span.FinishedSpan + +class TestSpanReporter() extends SpanReporter { + import scala.collection.JavaConverters._ + private val reportedSpans = new LinkedBlockingQueue[FinishedSpan]() + + override def reportSpans(spans: Seq[Span.FinishedSpan]): Unit = + reportedSpans.addAll(spans.asJava) + + def nextSpan(): Option[FinishedSpan] = + Option(reportedSpans.poll()) + + override def start(): Unit = {} + override def stop(): Unit = {} + override def reconfigure(config: Config): Unit = {} +} -- cgit v1.2.3