aboutsummaryrefslogtreecommitdiff
path: root/kamon-core-tests
diff options
context:
space:
mode:
authorIvan Topolnjak <ivantopo@gmail.com>2018-09-03 15:37:14 +0200
committerIvan Topolnjak <ivantopo@gmail.com>2018-09-03 15:37:14 +0200
commitc487c51a54e67944c80cf2aecc63ac8158bf99a6 (patch)
treed88669c3445fcdee659b62b0a8352f5ad50c1e9a /kamon-core-tests
parentd30ff29cdb5f94be34163d851d71716a316bdf10 (diff)
downloadKamon-c487c51a54e67944c80cf2aecc63ac8158bf99a6.tar.gz
Kamon-c487c51a54e67944c80cf2aecc63ac8158bf99a6.tar.bz2
Kamon-c487c51a54e67944c80cf2aecc63ac8158bf99a6.zip
wip on the HttpServer instrumentation
Diffstat (limited to 'kamon-core-tests')
-rw-r--r--kamon-core-tests/src/test/resources/reference.conf119
-rw-r--r--kamon-core-tests/src/test/scala/kamon/context/HttpPropagationSpec.scala30
-rw-r--r--kamon-core-tests/src/test/scala/kamon/instrumentation/HttpServerInstrumentationSpec.scala88
3 files changed, 222 insertions, 15 deletions
diff --git a/kamon-core-tests/src/test/resources/reference.conf b/kamon-core-tests/src/test/resources/reference.conf
index 0d7ae9e2..d165249e 100644
--- a/kamon-core-tests/src/test/resources/reference.conf
+++ b/kamon-core-tests/src/test/resources/reference.conf
@@ -2,4 +2,123 @@ kamon {
context.codecs.string-keys {
request-id = "X-Request-ID"
}
+}
+
+
+
+kamon {
+ instrumentation {
+ http-server {
+ noop {
+
+ #
+ # Configuration for HTTP context propagation
+ #
+ propagation {
+
+ # Enables or disables HTTP context propagation on this HTTP server instrumentation. Please note that if
+ # propagation is disabled then some distributed tracing features will not be work as expected (e.g. Spans can
+ # be created and reported but will not be linked across boundaries nor take trace identifiers from tags).
+ enabled = no
+
+ # HTTP propagation channel to b used by this instrumentation. Take a look at the kamon.propagation.http.default
+ # configuration for more details on how to configure the detault HTTP context propagation.
+ channel = "default"
+ }
+
+
+ #
+ # Configuration for HTTP server metrics collection
+ #
+ metrics {
+
+ # Enables collection of HTTP server metrics
+ enabled = no
+
+ # Tags to include on the HTTP server metrics. The available options are:
+ # - method: HTTP method from the request.
+ # - status-code: HTTP status code from the responses.
+ #
+ tags = [
+ "method",
+ "status-code"
+ ]
+ }
+
+
+ #
+ # Configuration for HTTP request tracing
+ #
+ tracing {
+
+ # Enables HTTP request tracing. When enabled the instrumentation will create Spans for incoming requests
+ # and finish them when the response is sent back to the clients.
+ enabled = no
+
+ # Select a context tag that provides a custom trace identifier. The custom trace identifier will be used
+ # only if all these conditions are met:
+ # - the context tag is present.
+ # - there is no parent Span on the incoming context (i.e. this is the first service on the trace).
+ # - the identifier is valued in accordance to the identity provider.
+ trace-id-tag = "none"
+
+ # Enables collection of span metrics using the `span.processing-time` metric.
+ span-metrics = on
+
+ # Select which tags should be included as span and span metric tags. The possible options are:
+ # - span: the tag is added as a Span tag (i.e. using span.tag(...))
+ # - metric: the tag is added a a Span metric tag (i.e. using span.tagMetric(...))
+ # - off: the tag is not used.
+ #
+ tags {
+
+ # Use the http.url tag.
+ url = span
+
+ # Use the http.method tag.
+ method = metric
+
+ # Use the http.status_code tag.
+ status-code = metric
+
+ # Copy tags from the context into the Spans with the specified purpouse.
+ from-context {
+
+ # The peer tag identifiest the service that is calling the current service. It is added by default with
+ # the HttpClient instrumentation.
+ peer = metric
+ }
+ }
+
+ # Custom mappings between routes and operation names.
+ operations {
+
+ # Operation name for Spans created on requests that could not be handled by any route in the current
+ # application.
+ unhandled = "unhandled"
+
+ # Provides custom mappings from HTTP paths into operation names. Meant to be used in cases where the bytecode
+ # instrumentation is not able to provide a sensible operation name that is free of high cardinality values.
+ # For example, with the following configuration:
+ # mappings {
+ # "/organization/*/user/*/profile" = "/organization/:orgID/user/:userID/profile"
+ # "/events/*/rsvps" = "EventRSVPs"
+ # }
+ #
+ # Requests to "/organization/3651/user/39652/profile" and "/organization/22234/user/54543/profile" will have
+ # the same operation name "/organization/:orgID/user/:userID/profile".
+ #
+ # Similarly, requests to "/events/aaa-bb-ccc/rsvps" and "/events/1234/rsvps" will have the same operation
+ # name "EventRSVPs".
+ #
+ # The patterns are expressed as globs and the operation names are free form.
+ #
+ mappings {
+
+ }
+ }
+ }
+ }
+ }
+ }
} \ No newline at end of file
diff --git a/kamon-core-tests/src/test/scala/kamon/context/HttpPropagationSpec.scala b/kamon-core-tests/src/test/scala/kamon/context/HttpPropagationSpec.scala
index 08d0b691..44165b98 100644
--- a/kamon-core-tests/src/test/scala/kamon/context/HttpPropagationSpec.scala
+++ b/kamon-core-tests/src/test/scala/kamon/context/HttpPropagationSpec.scala
@@ -12,7 +12,7 @@ class HttpPropagationSpec extends WordSpec with Matchers with OptionValues {
"The HTTP Context Propagation" when {
"reading from incoming requests" should {
"return an empty context if there are no tags nor keys" in {
- val context = httpPropagation.read(headerReaderFromMap(Map.empty))
+ val context = httpPropagation.readContext(headerReaderFromMap(Map.empty))
context.tags shouldBe empty
context.entries shouldBe empty
}
@@ -22,7 +22,7 @@ class HttpPropagationSpec extends WordSpec with Matchers with OptionValues {
"x-content-tags" -> "hello=world;correlation=1234",
"x-mapped-tag" -> "value"
)
- val context = httpPropagation.read(headerReaderFromMap(headers))
+ val context = httpPropagation.readContext(headerReaderFromMap(headers))
context.tags should contain only(
"hello" -> "world",
"correlation" -> "1234",
@@ -32,7 +32,7 @@ class HttpPropagationSpec extends WordSpec with Matchers with OptionValues {
"handle errors when reading HTTP headers" in {
val headers = Map("fail" -> "")
- val context = httpPropagation.read(headerReaderFromMap(headers))
+ val context = httpPropagation.readContext(headerReaderFromMap(headers))
context.tags shouldBe empty
context.entries shouldBe empty
}
@@ -44,7 +44,7 @@ class HttpPropagationSpec extends WordSpec with Matchers with OptionValues {
"integer-header" -> "123"
)
- val context = httpPropagation.read(headerReaderFromMap(headers))
+ val context = httpPropagation.readContext(headerReaderFromMap(headers))
context.get(HttpPropagationSpec.StringKey) shouldBe "hey"
context.get(HttpPropagationSpec.IntegerKey) shouldBe 123
context.get(HttpPropagationSpec.OptionalKey) shouldBe empty
@@ -66,7 +66,7 @@ class HttpPropagationSpec extends WordSpec with Matchers with OptionValues {
def propagationWritingTests(direction: Direction.Write) = {
"not write anything if the context is empty" in {
val headers = mutable.Map.empty[String, String]
- httpPropagation.write(Context.Empty, headerWriterFromMap(headers), direction)
+ httpPropagation.writeContext(Context.Empty, headerWriterFromMap(headers), direction)
headers shouldBe empty
}
@@ -77,7 +77,7 @@ class HttpPropagationSpec extends WordSpec with Matchers with OptionValues {
"mappedTag" -> "value"
))
- httpPropagation.write(context, headerWriterFromMap(headers), direction)
+ httpPropagation.writeContext(context, headerWriterFromMap(headers), direction)
headers should contain only(
"x-content-tags" -> "hello=world;",
"x-mapped-tag" -> "value"
@@ -91,7 +91,7 @@ class HttpPropagationSpec extends WordSpec with Matchers with OptionValues {
HttpPropagationSpec.IntegerKey, 42,
)
- httpPropagation.write(context, headerWriterFromMap(headers), direction)
+ httpPropagation.writeContext(context, headerWriterFromMap(headers), direction)
headers should contain only(
"string-header" -> "out-we-go"
)
@@ -123,7 +123,7 @@ class HttpPropagationSpec extends WordSpec with Matchers with OptionValues {
def headerReaderFromMap(map: Map[String, String]): HttpPropagation.HeaderReader = new HttpPropagation.HeaderReader {
- override def read(header: String): Option[String] = {
+ override def readHeader(header: String): Option[String] = {
if(map.get("fail").nonEmpty)
sys.error("failing on purpose")
@@ -132,7 +132,7 @@ class HttpPropagationSpec extends WordSpec with Matchers with OptionValues {
}
def headerWriterFromMap(map: mutable.Map[String, String]): HttpPropagation.HeaderWriter = new HttpPropagation.HeaderWriter {
- override def write(header: String, value: String): Unit = map.put(header, value)
+ override def writeHeader(header: String, value: String): Unit = map.put(header, value)
}
}
@@ -146,20 +146,20 @@ object HttpPropagationSpec {
class StringEntryCodec extends HttpPropagation.EntryReader with HttpPropagation.EntryWriter {
private val HeaderName = "string-header"
- override def read(reader: HttpPropagation.HeaderReader, context: Context): Context = {
- reader.read(HeaderName)
+ override def readEntry(reader: HttpPropagation.HeaderReader, context: Context): Context = {
+ reader.readHeader(HeaderName)
.map(v => context.withKey(StringKey, v))
.getOrElse(context)
}
- override def write(context: Context, writer: HttpPropagation.HeaderWriter, direction: Direction.Write): Unit = {
- Option(context.get(StringKey)).foreach(v => writer.write(HeaderName, v))
+ override def writeEntry(context: Context, writer: HttpPropagation.HeaderWriter, direction: Direction.Write): Unit = {
+ Option(context.get(StringKey)).foreach(v => writer.writeHeader(HeaderName, v))
}
}
class IntegerEntryCodec extends HttpPropagation.EntryReader {
- override def read(reader: HttpPropagation.HeaderReader, context: Context): Context = {
- reader.read("integer-header")
+ override def readEntry(reader: HttpPropagation.HeaderReader, context: Context): Context = {
+ reader.readHeader("integer-header")
.map(v => context.withKey(IntegerKey, v.toInt))
.getOrElse(context)
diff --git a/kamon-core-tests/src/test/scala/kamon/instrumentation/HttpServerInstrumentationSpec.scala b/kamon-core-tests/src/test/scala/kamon/instrumentation/HttpServerInstrumentationSpec.scala
new file mode 100644
index 00000000..a3662127
--- /dev/null
+++ b/kamon-core-tests/src/test/scala/kamon/instrumentation/HttpServerInstrumentationSpec.scala
@@ -0,0 +1,88 @@
+package kamon.instrumentation
+
+import kamon.context.Context
+import kamon.testkit.SpanInspection
+import org.scalatest.{Matchers, OptionValues, WordSpec}
+
+import scala.collection.mutable
+
+class HttpServerInstrumentationSpec extends WordSpec with Matchers with SpanInspection with OptionValues {
+
+ "the HTTP server instrumentation" when {
+ "configured for context propagation" should {
+ "read context entries and tags from the incoming request" in {
+ val httpServer = HttpServer.from("default", port = 8080, "http.server")
+ val handler = httpServer.handle(fakeRequest("http://localhost:8080/", "/", "GET", Map(
+ "context-tags" -> "tag=value;none=0011223344556677;",
+ "custom-trace-id" -> "0011223344556677"
+ )))
+
+ handler.context.tags should contain only(
+ "tag" -> "value",
+ "none" -> "0011223344556677"
+ )
+ }
+
+ "use the configured HTTP propagation channel" in {
+ val httpServer = HttpServer.from("default", port = 8080, "http.server")
+ val handler = httpServer.handle(fakeRequest("http://localhost:8080/", "/", "GET", Map(
+ "context-tags" -> "tag=value;none=0011223344556677;",
+ "custom-trace-id" -> "0011223344556677"
+ )))
+
+ handler.context.tags should contain only(
+ "tag" -> "value",
+ "none" -> "0011223344556677"
+ )
+
+ val span = inspect(handler.span)
+ span.context().traceID.string shouldNot be("0011223344556677")
+ span.tag("http.method").value shouldBe "GET"
+ span.tag("http.url").value shouldBe "http://localhost:8080/"
+
+ val responseHeaders = mutable.Map.empty[String, String]
+ handler.startResponse(fakeResponse(200, responseHeaders), handler.context.withTag("hello", "world"))
+
+ }
+ }
+
+ "when all capabilities are disabled" should {
+ "not read any context from the incoming requests" in {
+ val httpServer = HttpServer.from("noop", port = 8081, "http.server")
+ val handler = httpServer.handle(fakeRequest("http://localhost:8080/", "/", "GET", Map(
+ "context-tags" -> "tag=value;none=0011223344556677;",
+ "custom-trace-id" -> "0011223344556677"
+ )))
+
+ handler.context shouldBe Context.Empty
+ }
+
+ "not create any span to represent the server request" in {
+ val httpServer = HttpServer.from("noop", port = 8081, "http.server")
+ val handler = httpServer.handle(fakeRequest("http://localhost:8080/", "/", "GET", Map(
+ "context-tags" -> "tag=value;none=0011223344556677;",
+ "custom-trace-id" -> "0011223344556677"
+ )))
+
+ handler.span.isEmpty() shouldBe true
+ }
+
+ "not record any HTTP server metrics" is (pending)
+ }
+ }
+
+ def fakeRequest(requestUrl: String, requestPath: String, requestMethod: String, headers: Map[String, String]): HttpRequest =
+ new HttpRequest {
+ override def url: String = requestUrl
+ override def path: String = requestPath
+ override def method: String = requestMethod
+ override def readHeader(header: String): Option[String] = headers.get(header)
+ }
+
+ def fakeResponse(responseStatusCode: Int, headers: mutable.Map[String, String]): HttpResponse.Writable[HttpResponse] =
+ new HttpResponse.Writable[HttpResponse] {
+ override def statusCode: Int = responseStatusCode
+ override def writeHeader(header: String, value: String): Unit = headers.put(header, value)
+ override def build(): HttpResponse = this
+ }
+}