aboutsummaryrefslogtreecommitdiff
path: root/kamon-core
diff options
context:
space:
mode:
authorIvan Topolnjak <ivantopo@gmail.com>2017-07-19 09:35:30 +0200
committerIvan Topolnjak <ivantopo@gmail.com>2017-07-19 09:35:30 +0200
commit7cf98a6043cf90a17b5d0a51cf2399e35239cc0c (patch)
treeccff868acecf46381de6eaec498009bf8fc1999e /kamon-core
parentbfca826392933357046c5f4682cf7b43911b5433 (diff)
downloadKamon-7cf98a6043cf90a17b5d0a51cf2399e35239cc0c.tar.gz
Kamon-7cf98a6043cf90a17b5d0a51cf2399e35239cc0c.tar.bz2
Kamon-7cf98a6043cf90a17b5d0a51cf2399e35239cc0c.zip
tests for Span building and the ExtendedB3 codec, related bugfixes
Diffstat (limited to 'kamon-core')
-rw-r--r--kamon-core/src/main/resources/reference.conf2
-rw-r--r--kamon-core/src/main/scala/kamon/Kamon.scala1
-rw-r--r--kamon-core/src/main/scala/kamon/trace/IdentityProvider.scala9
-rw-r--r--kamon-core/src/main/scala/kamon/trace/Span.scala16
-rw-r--r--kamon-core/src/main/scala/kamon/trace/SpanContextCodec.scala38
-rw-r--r--kamon-core/src/main/scala/kamon/trace/Tracer.scala18
-rw-r--r--kamon-core/src/test/scala/kamon/testkit/SpanBuilding.scala20
-rw-r--r--kamon-core/src/test/scala/kamon/testkit/SpanInspector.scala52
-rw-r--r--kamon-core/src/test/scala/kamon/trace/ExtendedB3SpanContextCodecSpec.scala89
-rw-r--r--kamon-core/src/test/scala/kamon/trace/TracerSpec.scala154
10 files changed, 345 insertions, 54 deletions
diff --git a/kamon-core/src/main/resources/reference.conf b/kamon-core/src/main/resources/reference.conf
index 3e9d7b8d..dd42ab03 100644
--- a/kamon-core/src/main/resources/reference.conf
+++ b/kamon-core/src/main/resources/reference.conf
@@ -90,7 +90,7 @@ kamon {
# and server) of a RPC call, if you are reporting data to such systems then this option should be enabled.
#
# If you are using Zipkin, keep this option enabled. If you are using Jaeger, disable it.
- join-remote-parents-with-same-span-id = true
+ join-remote-parents-with-same-span-id = no
# Configures a sample that decides which traces should be reported to the trace backends. The possible values are:
# - always: report all traces.
diff --git a/kamon-core/src/main/scala/kamon/Kamon.scala b/kamon-core/src/main/scala/kamon/Kamon.scala
index a5df7e5a..fa9e78fe 100644
--- a/kamon-core/src/main/scala/kamon/Kamon.scala
+++ b/kamon-core/src/main/scala/kamon/Kamon.scala
@@ -55,6 +55,7 @@ object Kamon extends MetricLookup with ReporterRegistry with Tracer {
_filters = Filters.fromConfig(config)
_metrics.reconfigure(config)
_reporters.reconfigure(config)
+ _tracer.reconfigure(config)
_onReconfigureHooks.foreach(hook => {
Try(hook.onReconfigure(config)).failed.foreach(error =>
diff --git a/kamon-core/src/main/scala/kamon/trace/IdentityProvider.scala b/kamon-core/src/main/scala/kamon/trace/IdentityProvider.scala
index 0e7d3e7d..3f44629e 100644
--- a/kamon-core/src/main/scala/kamon/trace/IdentityProvider.scala
+++ b/kamon-core/src/main/scala/kamon/trace/IdentityProvider.scala
@@ -13,7 +13,14 @@ trait IdentityProvider {
}
object IdentityProvider {
- case class Identifier(string: String, bytes: Array[Byte])
+ case class Identifier(string: String, bytes: Array[Byte]) {
+
+ override def equals(obj: Any): Boolean = {
+ if(obj != null && obj.isInstanceOf[Identifier])
+ obj.asInstanceOf[Identifier].string == string
+ else false
+ }
+ }
val NoIdentifier = Identifier("", new Array[Byte](0))
diff --git a/kamon-core/src/main/scala/kamon/trace/Span.scala b/kamon-core/src/main/scala/kamon/trace/Span.scala
index ce09a36e..01cfbfc3 100644
--- a/kamon-core/src/main/scala/kamon/trace/Span.scala
+++ b/kamon-core/src/main/scala/kamon/trace/Span.scala
@@ -97,20 +97,20 @@ object Span {
*
* @param spanContext
* @param initialOperationName
- * @param initialTags
+ * @param initialSpanTags
* @param startTimestampMicros
* @param reporterRegistry
*/
- final class Real(spanContext: SpanContext, initialOperationName: String, initialTags: Map[String, Span.TagValue],
- startTimestampMicros: Long, reporterRegistry: ReporterRegistryImpl, tracer: Tracer) extends Span {
+ final class Real(spanContext: SpanContext, initialOperationName: String, initialSpanTags: Map[String, Span.TagValue],
+ initialMetricTags: Map[String, String], startTimestampMicros: Long, reporterRegistry: ReporterRegistryImpl, tracer: Tracer) extends Span {
private var collectMetrics: Boolean = true
private var open: Boolean = true
private val sampled: Boolean = spanContext.samplingDecision == SamplingDecision.Sample
private var operationName: String = initialOperationName
- private var spanTags: Map[String, Span.TagValue] = initialTags
- private var customMetricTags = Map.empty[String, String]
+ private var spanTags: Map[String, Span.TagValue] = initialSpanTags
+ private var customMetricTags = initialMetricTags
private var annotations = List.empty[Span.Annotation]
def annotate(annotation: Annotation): Span = synchronized {
@@ -201,9 +201,9 @@ object Span {
}
object Real {
- def apply(spanContext: SpanContext, initialOperationName: String, initialTags: Map[String, Span.TagValue],
- startTimestampMicros: Long, reporterRegistry: ReporterRegistryImpl, tracer: Tracer): Real =
- new Real(spanContext, initialOperationName, initialTags, startTimestampMicros, reporterRegistry, tracer)
+ def apply(spanContext: SpanContext, initialOperationName: String, initialSpanTags: Map[String, Span.TagValue],
+ initialMetricTags: Map[String, String], startTimestampMicros: Long, reporterRegistry: ReporterRegistryImpl, tracer: Tracer): Real =
+ new Real(spanContext, initialOperationName, initialSpanTags, initialMetricTags, startTimestampMicros, reporterRegistry, tracer)
}
sealed trait TagValue
diff --git a/kamon-core/src/main/scala/kamon/trace/SpanContextCodec.scala b/kamon-core/src/main/scala/kamon/trace/SpanContextCodec.scala
index 11d6de2c..43b5e8e4 100644
--- a/kamon-core/src/main/scala/kamon/trace/SpanContextCodec.scala
+++ b/kamon-core/src/main/scala/kamon/trace/SpanContextCodec.scala
@@ -44,9 +44,12 @@ object SpanContextCodec {
carrier.put(Headers.TraceIdentifier, urlEncode(spanContext.traceID.string))
carrier.put(Headers.SpanIdentifier, urlEncode(spanContext.spanID.string))
carrier.put(Headers.ParentSpanIdentifier, urlEncode(spanContext.parentID.string))
- carrier.put(Headers.Sampled, encodeSamplingDecision(spanContext.samplingDecision))
carrier.put(Headers.Baggage, encodeBaggage(spanContext.baggage))
+ encodeSamplingDecision(spanContext.samplingDecision).foreach { samplingDecision =>
+ carrier.put(Headers.Sampled, samplingDecision)
+ }
+
spanContext.baggage.get(Headers.Flags).foreach { flags =>
carrier.put(Headers.Flags, flags)
}
@@ -55,11 +58,8 @@ object SpanContextCodec {
carrier
}
- override def inject(spanContext: SpanContext): TextMap = {
- val mutableTextMap = TextMap.Default()
- inject(spanContext, mutableTextMap)
- mutableTextMap
- }
+ override def inject(spanContext: SpanContext): TextMap =
+ inject(spanContext, TextMap.Default())
override def extract(carrier: TextMap): Option[SpanContext] = {
val traceID = carrier.get(Headers.TraceIdentifier)
@@ -97,14 +97,16 @@ object SpanContextCodec {
if(baggage.getAll().nonEmpty) {
val encodedBaggage = new StringBuilder()
baggage.getAll().foreach {
- case (key, value) if key != Headers.Flags =>
- if(encodedBaggage.length() > 0)
- encodedBaggage.append(';')
-
- encodedBaggage
- .append(urlEncode(key))
- .append('=')
- .append(urlEncode(value))
+ case (key, value) =>
+ if(key != Headers.Flags) {
+ if (encodedBaggage.length() > 0)
+ encodedBaggage.append(';')
+
+ encodedBaggage
+ .append(urlEncode(key))
+ .append('=')
+ .append(urlEncode(value))
+ }
}
encodedBaggage.toString()
@@ -125,10 +127,10 @@ object SpanContextCodec {
baggage
}
- private def encodeSamplingDecision(samplingDecision: SamplingDecision): String = samplingDecision match {
- case SamplingDecision.Sample => "1"
- case SamplingDecision.DoNotSample => "0"
- case SamplingDecision.Unknown => ""
+ private def encodeSamplingDecision(samplingDecision: SamplingDecision): Option[String] = samplingDecision match {
+ case SamplingDecision.Sample => Some("1")
+ case SamplingDecision.DoNotSample => Some("0")
+ case SamplingDecision.Unknown => None
}
private def urlEncode(s: String): String = URLEncoder.encode(s, "UTF-8")
diff --git a/kamon-core/src/main/scala/kamon/trace/Tracer.scala b/kamon-core/src/main/scala/kamon/trace/Tracer.scala
index 47b633ac..714f0215 100644
--- a/kamon-core/src/main/scala/kamon/trace/Tracer.scala
+++ b/kamon-core/src/main/scala/kamon/trace/Tracer.scala
@@ -148,7 +148,8 @@ object Tracer {
final class SpanBuilder(operationName: String, tracer: Tracer.Default, reporterRegistry: ReporterRegistryImpl) {
private var parentContext: SpanContext = _
private var startTimestamp = 0L
- private var initialTags = Map.empty[String, Span.TagValue]
+ private var initialSpanTags = Map.empty[String, Span.TagValue]
+ private var initialMetricTags = Map.empty[String, String]
private var useActiveSpanAsParent = true
def asChildOf(parentContext: SpanContext): SpanBuilder = {
@@ -159,19 +160,24 @@ object Tracer {
def asChildOf(parentSpan: Span): SpanBuilder =
asChildOf(parentSpan.context())
+ def withMetricTag(key: String, value: String): SpanBuilder = {
+ this.initialMetricTags = this.initialMetricTags + (key -> value)
+ this
+ }
+
def withSpanTag(key: String, value: String): SpanBuilder = {
- this.initialTags = this.initialTags + (key -> TagValue.String(value))
+ this.initialSpanTags = this.initialSpanTags + (key -> TagValue.String(value))
this
}
def withSpanTag(key: String, value: Long): SpanBuilder = {
- this.initialTags = this.initialTags + (key -> TagValue.Number(value))
+ this.initialSpanTags = this.initialSpanTags + (key -> TagValue.Number(value))
this
}
def withSpanTag(key: String, value: Boolean): SpanBuilder = {
val tagValue = if (value) TagValue.True else TagValue.False
- this.initialTags = this.initialTags + (key -> tagValue)
+ this.initialSpanTags = this.initialSpanTags + (key -> tagValue)
this
}
@@ -195,7 +201,7 @@ object Tracer {
val samplingDecision: SamplingDecision = parentSpanContext
.map(_.samplingDecision)
.filter(_ != SamplingDecision.Unknown)
- .getOrElse(tracer.sampler.decide(operationName, initialTags))
+ .getOrElse(tracer.sampler.decide(operationName, initialSpanTags))
val spanContext = parentSpanContext match {
case Some(parent) => joinParentContext(parent, samplingDecision)
@@ -203,7 +209,7 @@ object Tracer {
}
tracer.tracerMetrics.createdSpans.increment()
- Span.Real(spanContext, operationName, initialTags, startTimestampMicros, reporterRegistry, tracer)
+ Span.Real(spanContext, operationName, initialSpanTags, initialMetricTags, startTimestampMicros, reporterRegistry, tracer)
}
private def joinParentContext(parent: SpanContext, samplingDecision: SamplingDecision): SpanContext =
diff --git a/kamon-core/src/test/scala/kamon/testkit/SpanBuilding.scala b/kamon-core/src/test/scala/kamon/testkit/SpanBuilding.scala
new file mode 100644
index 00000000..9b845ac9
--- /dev/null
+++ b/kamon-core/src/test/scala/kamon/testkit/SpanBuilding.scala
@@ -0,0 +1,20 @@
+package kamon.testkit
+
+import kamon.trace.SpanContext.{SamplingDecision, Source}
+import kamon.trace.{IdentityProvider, SpanContext, SpanContextCodec}
+
+trait SpanBuilding {
+ private val identityProvider = IdentityProvider.Default()
+ private val extendedB3Codec = SpanContextCodec.ExtendedB3(identityProvider)
+
+ def createSpanContext(samplingDecision: SamplingDecision = SamplingDecision.Sample): SpanContext =
+ SpanContext(
+ traceID = identityProvider.traceIdentifierGenerator().generate(),
+ spanID = identityProvider.spanIdentifierGenerator().generate(),
+ parentID = identityProvider.spanIdentifierGenerator().generate(),
+ samplingDecision = samplingDecision,
+ baggage = SpanContext.Baggage(),
+ source = Source.Local
+ )
+
+}
diff --git a/kamon-core/src/test/scala/kamon/testkit/SpanInspector.scala b/kamon-core/src/test/scala/kamon/testkit/SpanInspector.scala
index b0bb3e39..3ef1012b 100644
--- a/kamon-core/src/test/scala/kamon/testkit/SpanInspector.scala
+++ b/kamon-core/src/test/scala/kamon/testkit/SpanInspector.scala
@@ -1,15 +1,61 @@
package kamon.testkit
-import kamon.trace.Span
+import kamon.trace.{ActiveSpan, 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) = {
+ val realSpan = span match {
+ case _: Span.Real => span
+ case a: ActiveSpan =>
+ getField[ActiveSpan.Default, Span](a, "wrappedSpan")
+ }
+
+ val spanData = invoke[Span.Real, FinishedSpan](realSpan, "toFinishedSpan", classOf[Long] -> Long.box(Clock.microTimestamp()))
+ (realSpan, spanData)
+ }
+
+ def nonEmpty: Boolean =
+ !span.isInstanceOf[Span.Empty]
+
+ 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.Real, Map[String, String]](realSpan, "customMetricTags")
+ def startTimestamp(): Long =
+ getField[Span.Real, Long](realSpan, "startTimestampMicros")
- private def getSpanData(): Option[FinishedSpan] = {
-
+ 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 {
diff --git a/kamon-core/src/test/scala/kamon/trace/ExtendedB3SpanContextCodecSpec.scala b/kamon-core/src/test/scala/kamon/trace/ExtendedB3SpanContextCodecSpec.scala
index 9491181f..a11aaa4b 100644
--- a/kamon-core/src/test/scala/kamon/trace/ExtendedB3SpanContextCodecSpec.scala
+++ b/kamon-core/src/test/scala/kamon/trace/ExtendedB3SpanContextCodecSpec.scala
@@ -16,15 +16,19 @@
package kamon.trace
+import kamon.testkit.SpanBuilding
import kamon.trace.IdentityProvider.Identifier
-import kamon.trace.SpanContext.{SamplingDecision, Source}
+import kamon.trace.SpanContext.SamplingDecision
import org.scalatest.{Matchers, OptionValues, WordSpecLike}
-class ExtendedB3SpanContextCodecSpec extends WordSpecLike with Matchers with OptionValues {
+class ExtendedB3SpanContextCodecSpec extends WordSpecLike with Matchers with OptionValues with SpanBuilding {
+ val identityProvider = IdentityProvider.Default()
+ val extendedB3Codec = SpanContextCodec.ExtendedB3(identityProvider)
+
"The ExtendedB3 SpanContextCodec" should {
"return a TextMap containing the SpanContext data" in {
- val context = createSpanContext()
+ val context = testSpanContext()
context.baggage.add("some", "baggage")
context.baggage.add("more", "baggage")
@@ -37,7 +41,7 @@ class ExtendedB3SpanContextCodecSpec extends WordSpecLike with Matchers with Opt
}
"allow to provide the TextMap to be used for encoding" in {
- val context = createSpanContext()
+ val context = testSpanContext()
context.baggage.add("some", "baggage")
context.baggage.add("more", "baggage")
@@ -69,6 +73,36 @@ class ExtendedB3SpanContextCodecSpec extends WordSpecLike with Matchers with Opt
)
}
+ "decode the sampling decision based on the X-B3-Sampled header" in {
+ val sampledTextMap = TextMap.Default()
+ sampledTextMap.put("X-B3-TraceId", "1234")
+ sampledTextMap.put("X-B3-SpanId", "4321")
+ sampledTextMap.put("X-B3-Sampled", "1")
+
+ val notSampledTextMap = TextMap.Default()
+ notSampledTextMap.put("X-B3-TraceId", "1234")
+ notSampledTextMap.put("X-B3-SpanId", "4321")
+ notSampledTextMap.put("X-B3-Sampled", "0")
+
+ val noSamplingTextMap = TextMap.Default()
+ noSamplingTextMap.put("X-B3-TraceId", "1234")
+ noSamplingTextMap.put("X-B3-SpanId", "4321")
+
+ extendedB3Codec.extract(sampledTextMap).value.samplingDecision shouldBe SamplingDecision.Sample
+ extendedB3Codec.extract(notSampledTextMap).value.samplingDecision shouldBe SamplingDecision.DoNotSample
+ extendedB3Codec.extract(noSamplingTextMap).value.samplingDecision shouldBe SamplingDecision.Unknown
+ }
+
+ "not include the X-B3-Sampled header if the sampling decision is unknown" in {
+ val sampledSpanContext = testSpanContext()
+ val notSampledSpanContext = testSpanContext().copy(samplingDecision = SamplingDecision.DoNotSample)
+ val unknownSamplingSpanContext = testSpanContext().copy(samplingDecision = SamplingDecision.Unknown)
+
+ extendedB3Codec.inject(sampledSpanContext).get("X-B3-Sampled").value shouldBe("1")
+ extendedB3Codec.inject(notSampledSpanContext).get("X-B3-Sampled").value shouldBe("0")
+ extendedB3Codec.inject(unknownSamplingSpanContext).get("X-B3-Sampled") shouldBe empty
+ }
+
"use the Debug flag to override the sampling decision, if provided." in {
val textMap = TextMap.Default()
textMap.put("X-B3-TraceId", "1234")
@@ -103,6 +137,26 @@ class ExtendedB3SpanContextCodecSpec extends WordSpecLike with Matchers with Opt
spanContext.baggage.getAll() shouldBe empty
}
+ "do not extract a SpanContext if Trace ID and Span ID are not provided" in {
+ val onlyTraceID = TextMap.Default()
+ onlyTraceID.put("X-B3-TraceId", "1234")
+ onlyTraceID.put("X-B3-Sampled", "0")
+ onlyTraceID.put("X-B3-Flags", "1")
+
+ val onlySpanID = TextMap.Default()
+ onlySpanID.put("X-B3-SpanId", "4321")
+ onlySpanID.put("X-B3-Sampled", "0")
+ onlySpanID.put("X-B3-Flags", "1")
+
+ val noIds = TextMap.Default()
+ noIds.put("X-B3-Sampled", "0")
+ noIds.put("X-B3-Flags", "1")
+
+ extendedB3Codec.extract(onlyTraceID) shouldBe empty
+ extendedB3Codec.extract(onlySpanID) shouldBe empty
+ extendedB3Codec.extract(noIds) shouldBe empty
+ }
+
"round trip a SpanContext from TextMap -> SpanContext -> TextMap" in {
val textMap = TextMap.Default()
textMap.put("X-B3-TraceId", "1234")
@@ -118,7 +172,7 @@ class ExtendedB3SpanContextCodecSpec extends WordSpecLike with Matchers with Opt
}
"round trip a baggage that has special characters in there" in {
- val spanContext = createSpanContext()
+ val spanContext = testSpanContext()
spanContext.baggage.add("key-with-!specials", "value=with~spec;als")
val textMap = extendedB3Codec.inject(spanContext)
@@ -126,19 +180,26 @@ class ExtendedB3SpanContextCodecSpec extends WordSpecLike with Matchers with Opt
extractedSpanContext.baggage.getAll().values.toSeq should contain theSameElementsAs(spanContext.baggage.getAll().values.toSeq)
}
+ "internally carry the X-B3-Flags value so that it can be injected in outgoing requests" in {
+ val textMap = TextMap.Default()
+ textMap.put("X-B3-TraceId", "1234")
+ textMap.put("X-B3-ParentSpanId", "2222")
+ textMap.put("X-B3-SpanId", "4321")
+ textMap.put("X-B3-Sampled", "1")
+ textMap.put("X-B3-Flags", "1")
+ textMap.put("X-B3-Extra-Baggage", "some=baggage;more=baggage")
- }
+ val spanContext = extendedB3Codec.extract(textMap).value
+ val injectTextMap = extendedB3Codec.inject(spanContext)
- val identityProvider = IdentityProvider.Default()
- val extendedB3Codec = SpanContextCodec.ExtendedB3(identityProvider)
+ injectTextMap.get("X-B3-Flags").value shouldBe("1")
+ }
+ }
- def createSpanContext(samplingDecision: SamplingDecision = SamplingDecision.Sample): SpanContext =
- SpanContext(
+ def testSpanContext(): SpanContext =
+ createSpanContext().copy(
traceID = Identifier("1234", Array[Byte](1, 2, 3, 4)),
spanID = Identifier("4321", Array[Byte](4, 3, 2, 1)),
- parentID = Identifier("2222", Array[Byte](2, 2, 2, 2)),
- samplingDecision = samplingDecision,
- baggage = SpanContext.Baggage(),
- source = Source.Local
+ parentID = Identifier("2222", Array[Byte](2, 2, 2, 2))
)
} \ No newline at end of file
diff --git a/kamon-core/src/test/scala/kamon/trace/TracerSpec.scala b/kamon-core/src/test/scala/kamon/trace/TracerSpec.scala
index 686c15d0..3e05adb5 100644
--- a/kamon-core/src/test/scala/kamon/trace/TracerSpec.scala
+++ b/kamon-core/src/test/scala/kamon/trace/TracerSpec.scala
@@ -1,18 +1,166 @@
package kamon.trace
+import com.typesafe.config.ConfigFactory
import kamon.Kamon
-import org.scalatest.{Matchers, WordSpec}
+import kamon.testkit.{SpanBuilding, SpanInspector}
+import kamon.trace.Span.TagValue
+import kamon.trace.SpanContext.Source
+import kamon.trace.SpanContextCodec.Format
+import org.scalatest.{Matchers, OptionValues, WordSpec}
-class TracerSpec extends WordSpec with Matchers {
+class TracerSpec extends WordSpec with Matchers with SpanBuilding with OptionValues {
"the Kamon tracer" should {
- "build spans that contain all information given to the builder" in {
+ "construct a minimal Span that only has a operation name" in {
+ val span = tracer.buildSpan("myOperation").start()
+ val spanData = inspect(span)
+
+ spanData.operationName() shouldBe "myOperation"
+ spanData.metricTags() shouldBe empty
+ spanData.spanTags() shouldBe empty
+ }
+
+ "pass the operation name and tags to started Span" in {
val span = tracer.buildSpan("myOperation")
+ .withMetricTag("metric-tag", "value")
+ .withMetricTag("metric-tag", "value")
.withSpanTag("hello", "world")
+ .withSpanTag("kamon", "rulez")
+ .withSpanTag("number", 123)
+ .withSpanTag("boolean", true)
.start()
+
+ val spanData = inspect(span)
+ spanData.operationName() shouldBe "myOperation"
+ spanData.metricTags() should contain only (
+ ("metric-tag" -> "value"))
+
+ spanData.spanTags() should contain allOf(
+ ("hello" -> TagValue.String("world")),
+ ("kamon" -> TagValue.String("rulez")),
+ ("number" -> TagValue.Number(123)),
+ ("boolean" -> TagValue.True))
+ }
+
+ "do not interfere with the currently active Span if not requested when starting a Span" in {
+ val previouslyActiveSpan = tracer.activeSpan()
+ tracer.buildSpan("myOperation").start()
+ tracer.activeSpan() should be theSameInstanceAs(previouslyActiveSpan)
+ }
+
+ "make a span active with started with the .startActive() function and restore the previous Span when deactivated" in {
+ val previouslyActiveSpan = tracer.activeSpan()
+ val activeSpan = tracer.buildSpan("myOperation").startActive()
+
+ tracer.activeSpan() shouldNot be theSameInstanceAs(previouslyActiveSpan)
+ val activeSpanData = inspect(activeSpan)
+ activeSpanData.operationName() shouldBe "myOperation"
+
+ activeSpan.deactivate()
+ tracer.activeSpan() should be theSameInstanceAs(previouslyActiveSpan)
+ }
+
+ "not have any parent Span if there is ActiveSpan and no parent was explicitly given" in {
+ val span = tracer.buildSpan("myOperation").start()
+ val spanData = inspect(span)
+ spanData.context().parentID shouldBe IdentityProvider.NoIdentifier
+ }
+
+ "use the currently active span as parent" in {
+ val parent = tracer.buildSpan("myOperation").startActive()
+ val child = tracer.buildSpan("childOperation").asChildOf(parent).start()
+ parent.deactivate()
+
+ val parentData = inspect(parent)
+ val childData = inspect(child)
+ parentData.context().spanID shouldBe childData.context().parentID
+ }
+
+ "ignore the currently active span as parent if explicitly requested" in {
+ val parent = tracer.buildSpan("myOperation").startActive()
+ val child = tracer.buildSpan("childOperation").ignoreActiveSpan().start()
+ parent.deactivate()
+
+ val childData = inspect(child)
+ childData.context().parentID shouldBe IdentityProvider.NoIdentifier
+ }
+
+ "allow overriding the start timestamp for a Span" in {
+ val span = tracer.buildSpan("myOperation").withStartTimestamp(100).start()
+ val spanData = inspect(span)
+ spanData.startTimestamp() shouldBe 100
}
+
+ "inject and extract a SpanContext from a TextMap carrier" in {
+ val spanContext = createSpanContext()
+ val injected = Kamon.inject(spanContext, Format.TextMap)
+ val extractedSpanContext = Kamon.extract(Format.TextMap, injected).value
+
+ spanContext.traceID shouldBe(extractedSpanContext.traceID)
+ spanContext.spanID shouldBe(extractedSpanContext.spanID)
+ spanContext.parentID shouldBe(extractedSpanContext.parentID)
+ spanContext.baggage.getAll() shouldBe(extractedSpanContext.baggage.getAll())
+ }
+
+ "inject and extract a SpanContext from a TextMap carrier supplied by the caller" in {
+ val spanContext = createSpanContext()
+ val carrier = TextMap.Default()
+ Kamon.inject(spanContext, Format.TextMap, carrier)
+ val extractedSpanContext = Kamon.extract(Format.TextMap, carrier).value
+
+ spanContext.traceID shouldBe(extractedSpanContext.traceID)
+ spanContext.spanID shouldBe(extractedSpanContext.spanID)
+ spanContext.parentID shouldBe(extractedSpanContext.parentID)
+ spanContext.baggage.getAll() shouldBe(extractedSpanContext.baggage.getAll())
+ }
+
+ "inject and extract a SpanContext from a HttpHeaders carrier" in {
+ val spanContext = createSpanContext()
+ val injected = Kamon.inject(spanContext, Format.HttpHeaders)
+ val extractedSpanContext = Kamon.extract(Format.HttpHeaders, injected).value
+
+ spanContext.traceID shouldBe(extractedSpanContext.traceID)
+ spanContext.spanID shouldBe(extractedSpanContext.spanID)
+ spanContext.parentID shouldBe(extractedSpanContext.parentID)
+ spanContext.baggage.getAll() shouldBe(extractedSpanContext.baggage.getAll())
+ }
+
+ "inject and extract a SpanContext from a HttpHeaders using a TextMap provided by the caller" in {
+ val spanContext = createSpanContext()
+ val carrier = TextMap.Default()
+ Kamon.inject(spanContext, Format.HttpHeaders, carrier)
+ val extractedSpanContext = Kamon.extract(Format.HttpHeaders, carrier).value
+
+ spanContext.traceID shouldBe(extractedSpanContext.traceID)
+ spanContext.spanID shouldBe(extractedSpanContext.spanID)
+ spanContext.parentID shouldBe(extractedSpanContext.parentID)
+ spanContext.baggage.getAll() shouldBe(extractedSpanContext.baggage.getAll())
+ }
+
+
+ "preserve the same Span and Parent identifier when creating a Span with a remote parent if join-remote-parents-with-same-span-id is enabled" in {
+ val previousConfig = Kamon.config()
+
+ Kamon.reconfigure {
+ ConfigFactory.parseString("kamon.trace.join-remote-parents-with-same-span-id = yes")
+ .withFallback(Kamon.config())
+ }
+
+ val remoteParent = createSpanContext().copy(source = Source.Remote)
+ val childData = inspect(tracer.buildSpan("local").asChildOf(remoteParent).start())
+
+ childData.context().traceID shouldBe remoteParent.traceID
+ childData.context().parentID shouldBe remoteParent.parentID
+ childData.context().spanID shouldBe remoteParent.spanID
+
+ Kamon.reconfigure(previousConfig)
+ }
+
}
val tracer: Tracer = Kamon
+ def inspect(span: Span): SpanInspector =
+ SpanInspector(span)
+
}