aboutsummaryrefslogtreecommitdiff
path: root/kamon-core-tests/src/test/scala/kamon/trace
diff options
context:
space:
mode:
Diffstat (limited to 'kamon-core-tests/src/test/scala/kamon/trace')
-rw-r--r--kamon-core-tests/src/test/scala/kamon/trace/B3SpanCodecSpec.scala192
-rw-r--r--kamon-core-tests/src/test/scala/kamon/trace/DefaultIdentityGeneratorSpec.scala52
-rw-r--r--kamon-core-tests/src/test/scala/kamon/trace/DoubleLengthTraceIdentityGeneratorSpec.scala86
-rw-r--r--kamon-core-tests/src/test/scala/kamon/trace/LocalSpanSpec.scala100
-rw-r--r--kamon-core-tests/src/test/scala/kamon/trace/SpanMetrics.scala64
-rw-r--r--kamon-core-tests/src/test/scala/kamon/trace/TracerSpec.scala103
6 files changed, 597 insertions, 0 deletions
diff --git a/kamon-core-tests/src/test/scala/kamon/trace/B3SpanCodecSpec.scala b/kamon-core-tests/src/test/scala/kamon/trace/B3SpanCodecSpec.scala
new file mode 100644
index 00000000..e6fa283e
--- /dev/null
+++ b/kamon-core-tests/src/test/scala/kamon/trace/B3SpanCodecSpec.scala
@@ -0,0 +1,192 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2017 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.trace
+
+import kamon.context.{Context, TextMap}
+import kamon.testkit.SpanBuilding
+import kamon.trace.IdentityProvider.Identifier
+import kamon.trace.SpanContext.SamplingDecision
+import org.scalatest.{Matchers, OptionValues, WordSpecLike}
+
+
+class B3SpanCodecSpec extends WordSpecLike with Matchers with OptionValues with SpanBuilding {
+ val extendedB3Codec = SpanCodec.B3()
+
+ "The ExtendedB3 SpanContextCodec" should {
+ "return a TextMap containing the SpanContext data" in {
+ val context = testContext()
+
+ val textMap = extendedB3Codec.encode(context)
+ textMap.get("X-B3-TraceId").value shouldBe "1234"
+ textMap.get("X-B3-ParentSpanId").value shouldBe "2222"
+ textMap.get("X-B3-SpanId").value shouldBe "4321"
+ textMap.get("X-B3-Sampled").value shouldBe "1"
+ }
+
+
+ "not inject anything if there is no Span in the Context" in {
+ val textMap = extendedB3Codec.encode(Context.Empty)
+ textMap.values shouldBe empty
+ }
+
+ "extract a RemoteSpan from a TextMap when all fields are set" 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-Extra-Baggage", "some=baggage;more=baggage")
+
+ val spanContext = extendedB3Codec.decode(textMap, Context.Empty).get(Span.ContextKey).context()
+ spanContext.traceID.string shouldBe "1234"
+ spanContext.spanID.string shouldBe "4321"
+ spanContext.parentID.string shouldBe "2222"
+ spanContext.samplingDecision shouldBe SamplingDecision.Sample
+ }
+
+ "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.decode(sampledTextMap, Context.Empty)
+ .get(Span.ContextKey).context().samplingDecision shouldBe SamplingDecision.Sample
+
+ extendedB3Codec.decode(notSampledTextMap, Context.Empty)
+ .get(Span.ContextKey).context().samplingDecision shouldBe SamplingDecision.DoNotSample
+
+ extendedB3Codec.decode(noSamplingTextMap, Context.Empty)
+ .get(Span.ContextKey).context().samplingDecision shouldBe SamplingDecision.Unknown
+ }
+
+ "not include the X-B3-Sampled header if the sampling decision is unknown" in {
+ val context = testContext()
+ val sampledSpanContext = context.get(Span.ContextKey).context()
+ val notSampledSpanContext = Context.Empty.withKey(Span.ContextKey,
+ Span.Remote(sampledSpanContext.copy(samplingDecision = SamplingDecision.DoNotSample)))
+ val unknownSamplingSpanContext = Context.Empty.withKey(Span.ContextKey,
+ Span.Remote(sampledSpanContext.copy(samplingDecision = SamplingDecision.Unknown)))
+
+ extendedB3Codec.encode(context).get("X-B3-Sampled").value shouldBe("1")
+ extendedB3Codec.encode(notSampledSpanContext).get("X-B3-Sampled").value shouldBe("0")
+ extendedB3Codec.encode(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")
+ textMap.put("X-B3-SpanId", "4321")
+ textMap.put("X-B3-Sampled", "0")
+ textMap.put("X-B3-Flags", "1")
+
+ val spanContext = extendedB3Codec.decode(textMap, Context.Empty).get(Span.ContextKey).context()
+ spanContext.samplingDecision shouldBe SamplingDecision.Sample
+ }
+
+ "use the Debug flag as sampling decision when Sampled is not provided" in {
+ val textMap = TextMap.Default()
+ textMap.put("X-B3-TraceId", "1234")
+ textMap.put("X-B3-SpanId", "4321")
+ textMap.put("X-B3-Flags", "1")
+
+ val spanContext = extendedB3Codec.decode(textMap, Context.Empty).get(Span.ContextKey).context()
+ spanContext.samplingDecision shouldBe SamplingDecision.Sample
+ }
+
+ "extract a minimal SpanContext from a TextMap containing only the Trace ID and Span ID" in {
+ val textMap = TextMap.Default()
+ textMap.put("X-B3-TraceId", "1234")
+ textMap.put("X-B3-SpanId", "4321")
+
+ val spanContext = extendedB3Codec.decode(textMap, Context.Empty).get(Span.ContextKey).context()
+ spanContext.traceID.string shouldBe "1234"
+ spanContext.spanID.string shouldBe "4321"
+ spanContext.parentID shouldBe IdentityProvider.NoIdentifier
+ spanContext.samplingDecision shouldBe SamplingDecision.Unknown
+ }
+
+ "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.decode(onlyTraceID, Context.Empty).get(Span.ContextKey) shouldBe Span.Empty
+ extendedB3Codec.decode(onlySpanID, Context.Empty).get(Span.ContextKey) shouldBe Span.Empty
+ extendedB3Codec.decode(noIds, Context.Empty).get(Span.ContextKey) shouldBe Span.Empty
+ }
+
+ "round trip a Span from TextMap -> Context -> TextMap" 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")
+
+ val context = extendedB3Codec.decode(textMap, Context.Empty)
+ val injectTextMap = extendedB3Codec.encode(context)
+
+ textMap.values.toSeq should contain theSameElementsAs(injectTextMap.values.toSeq)
+ }
+
+ /*
+ // TODO: Should we be supporting this use case? maybe even have the concept of Debug requests ourselves?
+ "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")
+
+ val spanContext = extendedB3Codec.extract(textMap).value
+ val injectTextMap = extendedB3Codec.inject(spanContext)
+
+ injectTextMap.get("X-B3-Flags").value shouldBe("1")
+ }*/
+ }
+
+ def testContext(): Context = {
+ val 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))
+ )
+
+ Context.create().withKey(Span.ContextKey, Span.Remote(spanContext))
+ }
+
+} \ No newline at end of file
diff --git a/kamon-core-tests/src/test/scala/kamon/trace/DefaultIdentityGeneratorSpec.scala b/kamon-core-tests/src/test/scala/kamon/trace/DefaultIdentityGeneratorSpec.scala
new file mode 100644
index 00000000..8f9af7b0
--- /dev/null
+++ b/kamon-core-tests/src/test/scala/kamon/trace/DefaultIdentityGeneratorSpec.scala
@@ -0,0 +1,52 @@
+package kamon.trace
+
+import kamon.trace.IdentityProvider.Identifier
+import org.scalatest.{Matchers, OptionValues, WordSpecLike}
+import org.scalactic.TimesOnInt._
+
+class DefaultIdentityGeneratorSpec extends WordSpecLike with Matchers with OptionValues {
+ val idProvider = IdentityProvider.Default()
+ val traceGenerator = idProvider.traceIdGenerator()
+ val spanGenerator = idProvider.spanIdGenerator()
+
+ validateGenerator("TraceID Generator", traceGenerator)
+ validateGenerator("SpanID Generator", spanGenerator)
+
+ def validateGenerator(generatorName: String, generator: IdentityProvider.Generator) = {
+ s"The $generatorName" should {
+ "generate random longs (8 byte) identifiers" in {
+ 100 times {
+ val Identifier(string, bytes) = generator.generate()
+
+ string.length should be(16)
+ bytes.length should be(8)
+ }
+ }
+
+ "decode the string representation back into a identifier" in {
+ 100 times {
+ val identifier = generator.generate()
+ val decodedIdentifier = generator.from(identifier.string)
+
+ identifier.string should equal(decodedIdentifier.string)
+ identifier.bytes should equal(decodedIdentifier.bytes)
+ }
+ }
+
+ "decode the bytes representation back into a identifier" in {
+ 100 times {
+ val identifier = generator.generate()
+ val decodedIdentifier = generator.from(identifier.bytes)
+
+ identifier.string should equal(decodedIdentifier.string)
+ identifier.bytes should equal(decodedIdentifier.bytes)
+ }
+ }
+
+ "return IdentityProvider.NoIdentifier if the provided input cannot be decoded into a Identifier" in {
+ generator.from("zzzz") shouldBe(IdentityProvider.NoIdentifier)
+ generator.from(Array[Byte](1)) shouldBe(IdentityProvider.NoIdentifier)
+ }
+ }
+ }
+}
diff --git a/kamon-core-tests/src/test/scala/kamon/trace/DoubleLengthTraceIdentityGeneratorSpec.scala b/kamon-core-tests/src/test/scala/kamon/trace/DoubleLengthTraceIdentityGeneratorSpec.scala
new file mode 100644
index 00000000..b22f17e1
--- /dev/null
+++ b/kamon-core-tests/src/test/scala/kamon/trace/DoubleLengthTraceIdentityGeneratorSpec.scala
@@ -0,0 +1,86 @@
+package kamon.trace
+
+import kamon.trace.IdentityProvider.Identifier
+import org.scalactic.TimesOnInt._
+import org.scalatest.{Matchers, OptionValues, WordSpecLike}
+
+class DoubleLengthTraceIdentityGeneratorSpec extends WordSpecLike with Matchers with OptionValues {
+ val idProvider = IdentityProvider.DoubleSizeTraceID()
+ val traceGenerator = idProvider.traceIdGenerator()
+ val spanGenerator = idProvider.spanIdGenerator()
+
+ "The DoubleSizeTraceID identity provider" when {
+ "generating trace identifiers" should {
+ "generate random longs (16 byte) identifiers" in {
+ 100 times {
+ val Identifier(string, bytes) = traceGenerator.generate()
+
+ string.length should be(32)
+ bytes.length should be(16)
+ }
+ }
+
+ "decode the string representation back into a identifier" in {
+ 100 times {
+ val identifier = traceGenerator.generate()
+ val decodedIdentifier = traceGenerator.from(identifier.string)
+
+ identifier.string should equal(decodedIdentifier.string)
+ identifier.bytes should equal(decodedIdentifier.bytes)
+ }
+ }
+
+ "decode the bytes representation back into a identifier" in {
+ 100 times {
+ val identifier = traceGenerator.generate()
+ val decodedIdentifier = traceGenerator.from(identifier.bytes)
+
+ identifier.string should equal(decodedIdentifier.string)
+ identifier.bytes should equal(decodedIdentifier.bytes)
+ }
+ }
+
+ "return IdentityProvider.NoIdentifier if the provided input cannot be decoded into a Identifier" in {
+ traceGenerator.from("zzzz") shouldBe (IdentityProvider.NoIdentifier)
+ traceGenerator.from(Array[Byte](1)) shouldBe (IdentityProvider.NoIdentifier)
+ }
+ }
+
+ "generating span identifiers" should {
+ "generate random longs (8 byte) identifiers" in {
+ 100 times {
+ val Identifier(string, bytes) = spanGenerator.generate()
+
+ string.length should be(16)
+ bytes.length should be(8)
+ }
+ }
+
+ "decode the string representation back into a identifier" in {
+ 100 times {
+ val identifier = spanGenerator.generate()
+ val decodedIdentifier = spanGenerator.from(identifier.string)
+
+ identifier.string should equal(decodedIdentifier.string)
+ identifier.bytes should equal(decodedIdentifier.bytes)
+ }
+ }
+
+ "decode the bytes representation back into a identifier" in {
+ 100 times {
+ val identifier = spanGenerator.generate()
+ val decodedIdentifier = spanGenerator.from(identifier.bytes)
+
+ identifier.string should equal(decodedIdentifier.string)
+ identifier.bytes should equal(decodedIdentifier.bytes)
+ }
+ }
+
+ "return IdentityProvider.NoIdentifier if the provided input cannot be decoded into a Identifier" in {
+ spanGenerator.from("zzzz") shouldBe (IdentityProvider.NoIdentifier)
+ spanGenerator.from(Array[Byte](1)) shouldBe (IdentityProvider.NoIdentifier)
+ }
+ }
+ }
+
+}
diff --git a/kamon-core-tests/src/test/scala/kamon/trace/LocalSpanSpec.scala b/kamon-core-tests/src/test/scala/kamon/trace/LocalSpanSpec.scala
new file mode 100644
index 00000000..e24f8727
--- /dev/null
+++ b/kamon-core-tests/src/test/scala/kamon/trace/LocalSpanSpec.scala
@@ -0,0 +1,100 @@
+package kamon.trace
+
+import kamon.testkit.{MetricInspection, Reconfigure, TestSpanReporter}
+import kamon.util.Registration
+import kamon.Kamon
+import kamon.trace.Span.{Annotation, TagValue}
+import org.scalatest.concurrent.Eventually
+import org.scalatest.{BeforeAndAfterAll, Matchers, OptionValues, WordSpec}
+import org.scalatest.time.SpanSugar._
+
+class LocalSpanSpec extends WordSpec with Matchers with BeforeAndAfterAll with Eventually with OptionValues
+ with Reconfigure with MetricInspection {
+
+ "a real span" when {
+ "sampled and finished" should {
+ "be sent to the Span reporters" in {
+ Kamon.buildSpan("test-span")
+ .withSpanTag("test", "value")
+ .withStartTimestamp(100)
+ .start()
+ .finish(200)
+
+ eventually(timeout(2 seconds)) {
+ val finishedSpan = reporter.nextSpan().value
+ finishedSpan.operationName shouldBe("test-span")
+ finishedSpan.startTimestampMicros shouldBe 100
+ finishedSpan.endTimestampMicros shouldBe 200
+ finishedSpan.tags should contain("test" -> TagValue.String("value"))
+ }
+ }
+
+ "pass all the tags, annotations and baggage to the FinishedSpan instance when started and finished" in {
+ Kamon.buildSpan("full-span")
+ .withSpanTag("builder-string-tag", "value")
+ .withSpanTag("builder-boolean-tag-true", true)
+ .withSpanTag("builder-boolean-tag-false", false)
+ .withSpanTag("builder-number-tag", 42)
+ .withStartTimestamp(100)
+ .start()
+ .addSpanTag("span-string-tag", "value")
+ .addSpanTag("span-boolean-tag-true", true)
+ .addSpanTag("span-boolean-tag-false", false)
+ .addSpanTag("span-number-tag", 42)
+ .annotate("simple-annotation")
+ .annotate("regular-annotation", Map("data" -> "something"))
+ .annotate(4200, "custom-annotation-1", Map("custom" -> "yes-1"))
+ .annotate(Annotation(4201, "custom-annotation-2", Map("custom" -> "yes-2")))
+ .setOperationName("fully-populated-span")
+ .finish(200)
+
+ eventually(timeout(2 seconds)) {
+ val finishedSpan = reporter.nextSpan().value
+ finishedSpan.operationName shouldBe ("fully-populated-span")
+ finishedSpan.startTimestampMicros shouldBe 100
+ finishedSpan.endTimestampMicros shouldBe 200
+ finishedSpan.tags should contain allOf(
+ "builder-string-tag" -> TagValue.String("value"),
+ "builder-boolean-tag-true" -> TagValue.True,
+ "builder-boolean-tag-false" -> TagValue.False,
+ "builder-number-tag" -> TagValue.Number(42),
+ "span-string-tag" -> TagValue.String("value"),
+ "span-boolean-tag-true" -> TagValue.True,
+ "span-boolean-tag-false" -> TagValue.False,
+ "span-number-tag" -> TagValue.Number(42)
+ )
+
+ finishedSpan.annotations.length shouldBe (4)
+ val annotations = finishedSpan.annotations.groupBy(_.name)
+ annotations.keys should contain allOf(
+ "simple-annotation",
+ "regular-annotation",
+ "custom-annotation-1",
+ "custom-annotation-2"
+ )
+
+ val customAnnotationOne = annotations("custom-annotation-1").head
+ customAnnotationOne.timestampMicros shouldBe (4200)
+ customAnnotationOne.fields shouldBe (Map("custom" -> "yes-1"))
+
+ val customAnnotationTwo = annotations("custom-annotation-2").head
+ customAnnotationTwo.timestampMicros shouldBe (4201)
+ customAnnotationTwo.fields shouldBe (Map("custom" -> "yes-2"))
+ }
+ }
+ }
+ }
+
+ @volatile var registration: Registration = _
+ val reporter = new TestSpanReporter()
+
+ override protected def beforeAll(): Unit = {
+ enableFastSpanFlushing()
+ sampleAlways()
+ registration = Kamon.addReporter(reporter)
+ }
+
+ override protected def afterAll(): Unit = {
+ registration.cancel()
+ }
+}
diff --git a/kamon-core-tests/src/test/scala/kamon/trace/SpanMetrics.scala b/kamon-core-tests/src/test/scala/kamon/trace/SpanMetrics.scala
new file mode 100644
index 00000000..9ecffb24
--- /dev/null
+++ b/kamon-core-tests/src/test/scala/kamon/trace/SpanMetrics.scala
@@ -0,0 +1,64 @@
+package kamon.trace
+
+import kamon.Kamon
+import kamon.Kamon.buildSpan
+import kamon.metric._
+import org.scalatest.{Matchers, WordSpecLike}
+
+class SpanMetrics extends WordSpecLike with Matchers {
+ import SpanMetricsTestHelper._
+
+ val errorTag = "error" -> "true"
+ val histogramMetric: HistogramMetric = Kamon.histogram("span.elapsed-time")
+
+ "Span Metrics" should {
+ "be recorded for successeful execution" in {
+ val operation = "span-success"
+ val operationTag = "operation" -> operation
+
+ buildSpan(operation)
+ .start()
+ .finish()
+
+ val histogram = histogramMetric.refine(operationTag)
+ histogram.distribution().count === 1
+
+ val errorHistogram = histogramMetric.refine(Map(operationTag, errorTag)).distribution()
+ errorHistogram.count === 0
+
+ }
+
+ "record correctly error latency and count" in {
+ val operation = "span-failure"
+ val operationTag = "operation" -> operation
+
+ buildSpan(operation)
+ .start()
+ .addSpanTag("error", true)
+ .finish()
+
+ val histogram = histogramMetric.refine(operationTag)
+ histogram.distribution().count === 0
+
+ val errorHistogram = histogramMetric.refine(operationTag, errorTag).distribution()
+ errorHistogram.count === 1
+
+ }
+ }
+
+}
+
+object SpanMetricsTestHelper {
+
+ 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
+ }
+ }
+}
+
+
+
diff --git a/kamon-core-tests/src/test/scala/kamon/trace/TracerSpec.scala b/kamon-core-tests/src/test/scala/kamon/trace/TracerSpec.scala
new file mode 100644
index 00000000..fb5bb313
--- /dev/null
+++ b/kamon-core-tests/src/test/scala/kamon/trace/TracerSpec.scala
@@ -0,0 +1,103 @@
+package kamon.trace
+
+import com.typesafe.config.ConfigFactory
+import kamon.Kamon
+import kamon.context.Context
+import kamon.testkit.{SpanBuilding, SpanInspector}
+import kamon.trace.Span.TagValue
+import org.scalatest.{Matchers, OptionValues, WordSpec}
+
+class TracerSpec extends WordSpec with Matchers with SpanBuilding with OptionValues {
+
+ "the Kamon tracer" should {
+ "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))
+ }
+
+ "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
+ }
+
+
+ "automatically take the Span from the current Context as parent" in {
+ val parent = tracer.buildSpan("myOperation").start()
+ val child = Kamon.withContext(Context.create(Span.ContextKey, parent)) {
+ tracer.buildSpan("childOperation").asChildOf(parent).start()
+ }
+
+ 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").start()
+ val child = Kamon.withContext(Context.create(Span.ContextKey, parent)) {
+ tracer.buildSpan("childOperation").ignoreActiveSpan().start()
+ }
+
+ 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
+ }
+
+ "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 = Span.Remote(createSpanContext())
+ val childData = inspect(tracer.buildSpan("local").asChildOf(remoteParent).start())
+
+ childData.context().traceID shouldBe remoteParent.context.traceID
+ childData.context().parentID shouldBe remoteParent.context.parentID
+ childData.context().spanID shouldBe remoteParent.context.spanID
+
+ Kamon.reconfigure(previousConfig)
+ }
+
+ }
+
+ val tracer: Tracer = Kamon
+
+ def inspect(span: Span): SpanInspector =
+ SpanInspector(span)
+
+}