Skip to content

Commit 55a08fe

Browse files
authored
Add gzip and deflate encoding to metrics serving (#141)
1 parent a7f7aa4 commit 55a08fe

File tree

4 files changed

+58
-10
lines changed

4 files changed

+58
-10
lines changed

build.sbt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import com.typesafe.tools.mima.core.*
2+
13
// General info
24
val username = "RustedBones"
35
val repo = "pekko-http-metrics"
@@ -40,7 +42,14 @@ ThisBuild / tlFatalWarnings := true
4042
ThisBuild / tlJdkRelease := Some(8)
4143

4244
// mima
43-
ThisBuild / mimaBinaryIssueFilters ++= Seq()
45+
ThisBuild / mimaBinaryIssueFilters ++= Seq(
46+
ProblemFilters.exclude[IncompatibleResultTypeProblem](
47+
"fr.davit.pekko.http.metrics.core.scaladsl.server.HttpMetricsDirectives.metrics"
48+
),
49+
ProblemFilters.exclude[DirectMissingMethodProblem](
50+
"fr.davit.pekko.http.metrics.core.scaladsl.server.HttpMetricsDirectives.metrics"
51+
)
52+
)
4453

4554
lazy val `pekko-http-metrics` = (project in file("."))
4655
.aggregate(

core/src/main/scala/fr/davit/pekko/http/metrics/core/scaladsl/server/HttpMetricsDirectives.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@ import org.apache.pekko.http.scaladsl.marshalling.ToEntityMarshaller
2020
import org.apache.pekko.http.scaladsl.server.Directives._
2121
import org.apache.pekko.http.scaladsl.server.PathMatcher.{Matched, Unmatched}
2222
import org.apache.pekko.http.scaladsl.server.util.Tuple
23-
import org.apache.pekko.http.scaladsl.server.{Directive, Directive0, PathMatcher, StandardRoute}
23+
import org.apache.pekko.http.scaladsl.server.{Directive, Directive0, PathMatcher, Route}
2424
import fr.davit.pekko.http.metrics.core.{AttributeLabeler, HttpMetricsRegistry, PathLabeler}
2525

2626
trait HttpMetricsDirectives {
2727

28-
def metrics[T <: HttpMetricsRegistry: ToEntityMarshaller](registry: T): StandardRoute = complete(registry)
28+
def metrics[T <: HttpMetricsRegistry: ToEntityMarshaller](registry: T): Route =
29+
encodeResponse(complete(registry))
2930

3031
def metricsLabeled(labeler: AttributeLabeler, label: String): Directive0 =
3132
mapResponse(_.addAttribute(labeler.key, label))

core/src/test/scala/fr/davit/pekko/http/metrics/core/scaladsl/server/HttpMetricsDirectivesSpec.scala

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,35 @@
1616

1717
package fr.davit.pekko.http.metrics.core.scaladsl.server
1818

19-
import org.apache.pekko.http.scaladsl.marshalling.PredefinedToEntityMarshallers._
19+
import fr.davit.pekko.http.metrics.core.AttributeLabeler
20+
import fr.davit.pekko.http.metrics.core.PathLabeler
21+
import fr.davit.pekko.http.metrics.core.TestRegistry
22+
import org.apache.pekko.http.scaladsl.marshalling.PredefinedToEntityMarshallers.*
23+
import org.apache.pekko.http.scaladsl.model.HttpResponse
2024
import org.apache.pekko.http.scaladsl.model.StatusCodes
21-
import org.apache.pekko.http.scaladsl.server.Directives._
25+
import org.apache.pekko.http.scaladsl.model.headers.HttpEncoding
26+
import org.apache.pekko.http.scaladsl.model.headers.HttpEncodings
27+
import org.apache.pekko.http.scaladsl.model.headers.`Accept-Encoding`
28+
import org.apache.pekko.http.scaladsl.model.headers.`Content-Encoding`
29+
import org.apache.pekko.http.scaladsl.server.Directives.*
2230
import org.apache.pekko.http.scaladsl.testkit.ScalatestRouteTest
23-
import fr.davit.pekko.http.metrics.core.{AttributeLabeler, PathLabeler, TestRegistry}
2431
import org.scalatest.flatspec.AnyFlatSpec
32+
import org.scalatest.matchers.Matcher
2533
import org.scalatest.matchers.should.Matchers
34+
import org.apache.pekko.http.scaladsl.coding.Coders
35+
import org.apache.pekko.http.scaladsl.unmarshalling.Unmarshal
36+
import scala.concurrent.Await
37+
import scala.concurrent.duration.*
2638

2739
class HttpMetricsDirectivesSpec extends AnyFlatSpec with Matchers with ScalatestRouteTest {
2840

2941
import HttpMetricsDirectives._
3042

43+
def haveNoContentEncoding: Matcher[HttpResponse] =
44+
be(None).compose { (_: HttpResponse).header[`Content-Encoding`] }
45+
def haveContentEncoding(encoding: HttpEncoding): Matcher[HttpResponse] =
46+
be(Some(`Content-Encoding`(encoding))).compose { (_: HttpResponse).header[`Content-Encoding`] }
47+
3148
"HttpMetricsDirectives" should "expose the registry" in {
3249
implicit val marshaller = StringMarshaller.compose[TestRegistry](r => s"active: ${r.requestsActive.value()}")
3350
val registry = new TestRegistry()
@@ -38,6 +55,29 @@ class HttpMetricsDirectivesSpec extends AnyFlatSpec with Matchers with Scalatest
3855
}
3956

4057
Get("/metrics") ~> route ~> check {
58+
response should haveNoContentEncoding
59+
responseAs[String] shouldBe "active: 1"
60+
}
61+
62+
// gzip
63+
Get("/metrics") ~> `Accept-Encoding`(HttpEncodings.gzip) ~> route ~> check {
64+
response should haveContentEncoding(HttpEncodings.gzip)
65+
val decodedResponse = Coders.Gzip.decodeMessage(response)
66+
val data = Await.result(Unmarshal(decodedResponse).to[String], 1.second)
67+
data shouldBe "active: 1"
68+
}
69+
70+
// deflate
71+
Get("/metrics") ~> `Accept-Encoding`(HttpEncodings.deflate) ~> route ~> check {
72+
response should haveContentEncoding(HttpEncodings.deflate)
73+
val decodedResponse = Coders.Deflate.decodeMessage(response)
74+
val data = Await.result(Unmarshal(decodedResponse).to[String], 1.second)
75+
data shouldBe "active: 1"
76+
}
77+
78+
// unknown -> accept and skip encoding
79+
Get("/metrics") ~> `Accept-Encoding`(HttpEncodings.`x-zip`) ~> route ~> check {
80+
response should haveNoContentEncoding
4181
responseAs[String] shouldBe "active: 1"
4282
}
4383
}

prometheus/src/test/scala/fr/davit/pekko/http/metrics/prometheus/marshalling/PrometheusMarshallersSpec.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,7 @@ class PrometheusMarshallersSpec extends AnyFlatSpec with Matchers with Scalatest
9595
}
9696

9797
it should "expose metrics as prometheus open-metrics format" in new Fixture {
98-
val request = Get().addHeader(Accept(PrometheusMarshallers.OpenMetricsContentType.mediaType))
99-
request ~> metrics(registry) ~> check {
98+
Get() ~> Accept(PrometheusMarshallers.OpenMetricsContentType.mediaType) ~> metrics(registry) ~> check {
10099
response.entity.contentType shouldBe PrometheusMarshallers.OpenMetricsContentType
101100
val text = responseAs[String]
102101
// println(text)
@@ -129,8 +128,7 @@ class PrometheusMarshallersSpec extends AnyFlatSpec with Matchers with Scalatest
129128
it should "expose metrics as prometheus protobuf format" in new Fixture {
130129
import io.prometheus.metrics.expositionformats.generated.com_google_protobuf_4_31_0.Metrics
131130

132-
val request = Get().addHeader(Accept(PrometheusMarshallers.ProtobufContentType.mediaType))
133-
request ~> metrics(registry) ~> check {
131+
Get() ~> Accept(PrometheusMarshallers.ProtobufContentType.mediaType) ~> metrics(registry) ~> check {
134132
response.entity.contentType shouldBe PrometheusMarshallers.ProtobufContentType
135133
val is = response.entity.dataBytes.runWith(StreamConverters.asInputStream())
136134
try {

0 commit comments

Comments
 (0)