From 92876faf942e65eeece30fd9ab89f70759b79dab Mon Sep 17 00:00:00 2001 From: Ivan Topolnjak Date: Mon, 17 Sep 2018 12:00:37 +0200 Subject: create HTTP server metrics tests --- .../HttpServerInstrumentationSpec.scala | 151 ++++++++++++++++++++- 1 file changed, 149 insertions(+), 2 deletions(-) (limited to 'kamon-core-tests') diff --git a/kamon-core-tests/src/test/scala/kamon/instrumentation/HttpServerInstrumentationSpec.scala b/kamon-core-tests/src/test/scala/kamon/instrumentation/HttpServerInstrumentationSpec.scala index d469725f..19e08666 100644 --- a/kamon-core-tests/src/test/scala/kamon/instrumentation/HttpServerInstrumentationSpec.scala +++ b/kamon-core-tests/src/test/scala/kamon/instrumentation/HttpServerInstrumentationSpec.scala @@ -1,13 +1,16 @@ package kamon.instrumentation +import java.time.Duration + import kamon.context.Context -import kamon.metric.Counter +import kamon.metric.{Counter, Histogram, RangeSampler} import kamon.testkit.{MetricInspection, SpanInspection} +import org.scalatest.concurrent.Eventually import org.scalatest.{Matchers, OptionValues, WordSpec} import scala.collection.mutable -class HttpServerInstrumentationSpec extends WordSpec with Matchers with SpanInspection with OptionValues with MetricInspection { +class HttpServerInstrumentationSpec extends WordSpec with Matchers with SpanInspection with OptionValues with MetricInspection with Eventually { "the HTTP server instrumentation" when { "configured for context propagation" should { @@ -21,6 +24,9 @@ class HttpServerInstrumentationSpec extends WordSpec with Matchers with SpanInsp "tag" -> "value", "none" -> "0011223344556677" ) + + handler.send(fakeResponse(200, mutable.Map.empty), Context.Empty) + handler.doneSending(0L) } "use the configured HTTP propagation channel" in { @@ -41,7 +47,130 @@ class HttpServerInstrumentationSpec extends WordSpec with Matchers with SpanInsp val responseHeaders = mutable.Map.empty[String, String] handler.send(fakeResponse(200, responseHeaders), handler.context.withTag("hello", "world")) + handler.doneSending(0L) + } + } + + "configured for HTTP server metrics" should { + "track the number of open connections" in { + openConnections(8081).distribution() + + httpServer().openConnection() + httpServer().openConnection() + val snapshotWithOpenConnections = openConnections(8081).distribution() + snapshotWithOpenConnections.min shouldBe 0 + snapshotWithOpenConnections.max shouldBe 2 + + httpServer().closeConnection(Duration.ofSeconds(20), 10) + httpServer().closeConnection(Duration.ofSeconds(30), 15) + + eventually { + val snapshotWithoutOpenConnections = openConnections(8081).distribution() + snapshotWithoutOpenConnections.min shouldBe 0 + snapshotWithoutOpenConnections.max shouldBe 0 + } + } + + "track the distribution of number of requests handled per each connection" in { + connectionUsage(8081).distribution() + + httpServer().openConnection() + httpServer().openConnection() + httpServer().closeConnection(Duration.ofSeconds(20), 10) + httpServer().closeConnection(Duration.ofSeconds(30), 15) + + val connectionUsageSnapshot = connectionUsage(8081).distribution() + connectionUsageSnapshot.buckets.map(_.value) should contain allOf( + 10, + 15 + ) + } + + "track the distribution of connection lifetime across all connections" in { + connectionLifetime(8081).distribution() + + httpServer().openConnection() + httpServer().openConnection() + httpServer().closeConnection(Duration.ofSeconds(20), 10) + httpServer().closeConnection(Duration.ofSeconds(30), 15) + + val connectionLifetimeSnapshot = connectionLifetime(8081).distribution() + connectionLifetimeSnapshot.buckets.map(_.value) should contain allOf( + 19998441472L, // 20 seconds with 1% precision + 29930553344L // 30 seconds with 1% precision + ) + } + + "track the number of active requests" in { + activeRequests(8081).distribution() + + val handlerOne = httpServer().receive(fakeRequest("http://localhost:8080/", "/", "GET", Map.empty)) + val handlerTwo = httpServer().receive(fakeRequest("http://localhost:8080/", "/", "GET", Map.empty)) + + val snapshotWithActiveRequests = activeRequests(8081).distribution() + snapshotWithActiveRequests.min shouldBe 0 + snapshotWithActiveRequests.max shouldBe 2 + + handlerOne.send(fakeResponse(200, mutable.Map.empty), Context.Empty) + handlerTwo.send(fakeResponse(200, mutable.Map.empty), Context.Empty) + handlerOne.doneSending(0L) + handlerTwo.doneSending(0L) + + eventually { + val snapshotWithoutActiveRequests = activeRequests(8081).distribution() + snapshotWithoutActiveRequests.min shouldBe 0 + snapshotWithoutActiveRequests.max shouldBe 0 + } + } + + "track the distribution of sizes on incoming requests" in { + requestSize(8081).distribution() + + httpServer().receive(fakeRequest("http://localhost:8080/", "/", "GET", Map.empty)).doneReceiving(300) + httpServer().receive(fakeRequest("http://localhost:8080/", "/", "GET", Map.empty)).doneReceiving(400) + + val requestSizeSnapshot = requestSize(8081).distribution() + requestSizeSnapshot.buckets.map(_.value) should contain allOf( + 300, + 400 + ) + } + + "track the distribution of sizes on outgoing responses" in { + responseSize(8081).distribution() + + httpServer().receive(fakeRequest("http://localhost:8080/", "/", "GET", Map.empty)).doneSending(300) + httpServer().receive(fakeRequest("http://localhost:8080/", "/", "GET", Map.empty)).doneSending(400) + + val requestSizeSnapshot = responseSize(8081).distribution() + requestSizeSnapshot.buckets.map(_.value) should contain allOf( + 300, + 400 + ) + } + + "track the number of responses per status code" in { + // resets all counters + completedRequests(8081, 100).value() + completedRequests(8081, 200).value() + completedRequests(8081, 300).value() + completedRequests(8081, 400).value() + completedRequests(8081, 500).value() + + + val request = fakeRequest("http://localhost:8080/", "/", "GET", Map.empty) + httpServer().receive(request).send(fakeResponse(200, mutable.Map.empty), Context.Empty) + httpServer().receive(request).send(fakeResponse(302, mutable.Map.empty), Context.Empty) + httpServer().receive(request).send(fakeResponse(404, mutable.Map.empty), Context.Empty) + httpServer().receive(request).send(fakeResponse(504, mutable.Map.empty), Context.Empty) + httpServer().receive(request).send(fakeResponse(110, mutable.Map.empty), Context.Empty) + + completedRequests(8081, 100).value() shouldBe 1L + completedRequests(8081, 200).value() shouldBe 1L + completedRequests(8081, 300).value() shouldBe 1L + completedRequests(8081, 400).value() shouldBe 1L + completedRequests(8081, 500).value() shouldBe 1L } } @@ -165,4 +294,22 @@ class HttpServerInstrumentationSpec extends WordSpec with Matchers with SpanInsp } } + def openConnections(port: Int): RangeSampler = + HttpServer.Metrics.of(TestComponent, TestInterface, port).openConnections + + def connectionUsage(port: Int): Histogram = + HttpServer.Metrics.of(TestComponent, TestInterface, port).connectionUsage + + def connectionLifetime(port: Int): Histogram = + HttpServer.Metrics.of(TestComponent, TestInterface, port).connectionLifetime + + def activeRequests(port: Int): RangeSampler = + HttpServer.Metrics.of(TestComponent, TestInterface, port).activeRequests + + def requestSize(port: Int): Histogram = + HttpServer.Metrics.of(TestComponent, TestInterface, port).requestSize + + def responseSize(port: Int): Histogram = + HttpServer.Metrics.of(TestComponent, TestInterface, port).responseSize + } -- cgit v1.2.3