Skip to content

Commit

Permalink
Use otel4s-semconv-metrics-experimental for semantic testing
Browse files Browse the repository at this point in the history
  • Loading branch information
iRevive authored and NthPortal committed Oct 8, 2024
1 parent 9e81a80 commit 904b670
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 4 deletions.
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,12 @@ lazy val metrics = crossProject(JVMPlatform, JSPlatform, NativePlatform)
.settings(
name := s"$baseName-metrics",
libraryDependencies ++= Seq(
"org.http4s" %%% "http4s-core" % http4sV,
"org.http4s" %%% "http4s-client" % http4sV,
"org.typelevel" %%% "otel4s-core-common" % otel4sV,
"org.typelevel" %%% "otel4s-core-metrics" % otel4sV,
"org.typelevel" %%% "otel4s-semconv" % otel4sV,
"org.http4s" %%% "http4s-server" % http4sV % Test,
"org.typelevel" %%% "otel4s-semconv-metrics-experimental" % otel4sV % Test,
),
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ object OtelMetrics {
Meter[F]
.upDownCounter[Long](s"http.$kind.active_requests")
.withUnit("{request}")
.withDescription("Number of active HTTP requests.")
.withDescription(s"Number of active HTTP $kind requests.")
.create

val abnormalTerminations: F[Histogram[F, Double]] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,19 @@ package otel4s.middleware.metrics
import cats.data.OptionT
import cats.effect.IO
import munit.CatsEffectSuite
import org.http4s.server.middleware.Metrics
import org.http4s.client.Client
import org.http4s.client.middleware.{Metrics => ClientMetrics}
import org.http4s.server.middleware.{Metrics => ServerMetrics}
import org.typelevel.otel4s.Attributes
import org.typelevel.otel4s.metrics.Meter
import org.typelevel.otel4s.sdk.metrics.data.MetricData
import org.typelevel.otel4s.sdk.metrics.data.MetricPoints
import org.typelevel.otel4s.sdk.metrics.data.PointData
import org.typelevel.otel4s.sdk.testkit.metrics.MetricsTestkit
import org.typelevel.otel4s.semconv.MetricSpec
import org.typelevel.otel4s.semconv.Requirement
import org.typelevel.otel4s.semconv.experimental.metrics.HttpExperimentalMetrics
import org.typelevel.otel4s.semconv.metrics.HttpMetrics

class OtelMetricsTests extends CatsEffectSuite {
test("OtelMetrics") {
Expand All @@ -41,7 +48,7 @@ class OtelMetricsTests extends CatsEffectSuite {
_ <- {
val fakeServer =
HttpRoutes[IO](e => OptionT.liftF(e.body.compile.drain.as(Response[IO](Status.Ok))))
val meteredServer = Metrics[IO](metricsOps)(fakeServer)
val meteredServer = ServerMetrics[IO](metricsOps)(fakeServer)

meteredServer
.run(Request[IO](Method.GET))
Expand Down Expand Up @@ -101,4 +108,77 @@ class OtelMetricsTests extends CatsEffectSuite {
}
}
}

test("server semantic test") {
val specs = List(
HttpMetrics.ServerRequestDuration,
HttpExperimentalMetrics.ServerActiveRequests,
)

MetricsTestkit.inMemory[IO]().use { testkit =>
testkit.meterProvider.get("meter").flatMap { implicit meter =>
val fakeServer =
HttpRoutes[IO](e => OptionT.liftF(e.body.compile.drain.as(Response[IO](Status.Ok))))

for {
metricsOps <- OtelMetrics.serverMetricsOps[IO]()
server = ServerMetrics[IO](metricsOps)(fakeServer)

_ <- server.run(Request[IO](Method.GET)).semiflatMap(_.body.compile.drain).value
metrics <- testkit.collectMetrics
} yield specs.foreach(spec => specTest(metrics, spec))
}
}
}

test("client semantic test") {
val specs = List(
HttpMetrics.ClientRequestDuration,
HttpExperimentalMetrics.ClientActiveRequests,
)

MetricsTestkit.inMemory[IO]().use { testkit =>
testkit.meterProvider.get("meter").flatMap { implicit meter =>
val fakeServer =
HttpRoutes[IO](e => OptionT.liftF(e.body.compile.drain.as(Response[IO](Status.Ok))))

for {
clientOps <- OtelMetrics.clientMetricsOps[IO]()
client = ClientMetrics[IO](clientOps)(Client.fromHttpApp(fakeServer.orNotFound))

_ <- client.run(Request[IO](Method.GET)).use(_.body.compile.drain)
metrics <- testkit.collectMetrics
} yield specs.foreach(spec => specTest(metrics, spec))
}
}
}

private def specTest(metrics: List[MetricData], spec: MetricSpec): Unit = {
val metric = metrics.find(_.name == spec.name)
assert(
metric.isDefined,
s"${spec.name} metric is missing. Available [${metrics.map(_.name).mkString(", ")}]",
)

val clue = s"[${spec.name}] has a mismatched property"

metric.foreach { md =>
assertEquals(md.name, spec.name, clue)
assertEquals(md.description, Some(spec.description), clue)
assertEquals(md.unit, Some(spec.unit), clue)

val required = spec.attributeSpecs
.filter(_.requirement.level == Requirement.Level.Required)
.map(_.key)
.toSet

val current = md.data.points.toVector
.flatMap(_.attributes.map(_.key))
.filter(key => required.contains(key))
.toSet

assertEquals(current, required, clue)
}
}

}

0 comments on commit 904b670

Please sign in to comment.