Skip to content

Commit

Permalink
refactor!: prometheus and OTEL defaul Metric formats
Browse files Browse the repository at this point in the history
  • Loading branch information
varshith257 authored Oct 30, 2024
1 parent dc6c51e commit 453389b
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,23 @@ import OpenTelemetryMetrics._
import io.opentelemetry.api.OpenTelemetry
import sttp.tapir.model.ServerRequest
import sttp.tapir.server.interceptor.metrics.MetricsRequestInterceptor
import sttp.tapir.server.metrics.{EndpointMetric, Metric, MetricLabels}
import sttp.tapir.server.metrics.{EndpointMetric, Metric, MetricLabels, OTELMetricLabels}
import sttp.tapir.server.model.ServerResponse

import java.time.{Duration, Instant}

case class OpenTelemetryMetrics[F[_]](meter: Meter, metrics: List[Metric[F, _]]) {

/** Registers a `request_active{path, method}` up-down-counter (assuming default labels). */
def addRequestsActive(labels: MetricLabels = MetricLabels.Default): OpenTelemetryMetrics[F] =
def addRequestsActive(labels: MetricLabels = OTELMetricLabels.Default): OpenTelemetryMetrics[F] =
copy(metrics = metrics :+ requestActive(meter, labels))

/** Registers a `request_total{path, method, status}` counter (assuming default labels). */
def addRequestsTotal(labels: MetricLabels = MetricLabels.Default): OpenTelemetryMetrics[F] =
def addRequestsTotal(labels: MetricLabels = OTELMetricLabels.Default): OpenTelemetryMetrics[F] =
copy(metrics = metrics :+ requestTotal(meter, labels))

/** Registers a `request_duration_seconds{path, method, status, phase}` histogram (assuming default labels). */
def addRequestsDuration(labels: MetricLabels = MetricLabels.Default): OpenTelemetryMetrics[F] =
def addRequestsDuration(labels: MetricLabels = OTELMetricLabels.Default): OpenTelemetryMetrics[F] =
copy(metrics = metrics :+ requestDuration(meter, labels))

/** Registers a custom metric. */
Expand All @@ -50,7 +50,7 @@ object OpenTelemetryMetrics {
* measured separately up to the point where the headers are determined, and then once again when the whole response body is complete.
*/
def default[F[_]](otel: OpenTelemetry): OpenTelemetryMetrics[F] =
default(defaultMeter(otel), MetricLabels.Default)
default(defaultMeter(otel), OTELMetricLabels.Default)

/** Registers default metrics (see other variants) using custom labels. */
def default[F[_]](otel: OpenTelemetry, labels: MetricLabels): OpenTelemetryMetrics[F] = default(defaultMeter(otel), labels)
Expand All @@ -64,10 +64,10 @@ object OpenTelemetryMetrics {
* Status is by default the status code class (1xx, 2xx, etc.), and phase can be either `headers` or `body` - request duration is
* measured separately up to the point where the headers are determined, and then once again when the whole response body is complete.
*/
def default[F[_]](meter: Meter): OpenTelemetryMetrics[F] = default(meter, MetricLabels.Default)
def default[F[_]](meter: Meter): OpenTelemetryMetrics[F] = default(meter, OTELMetricLabels.Default)

/** Registers default metrics (see other variants) using custom labels. */
def default[F[_]](meter: Meter, labels: MetricLabels = MetricLabels.Default): OpenTelemetryMetrics[F] =
def default[F[_]](meter: Meter, labels: MetricLabels = OTELMetricLabels.Default): OpenTelemetryMetrics[F] =
OpenTelemetryMetrics(
meter,
List[Metric[F, _]](
Expand All @@ -87,21 +87,8 @@ object OpenTelemetryMetrics {
onRequest = (req, counter, m) => {
m.unit {
EndpointMetric()
.onEndpointRequest { ep =>
m.eval {
val attrs = asOpenTelemetryAttributes(labels, ep, req)
println(s"[DEBUG] Incrementing active requests with attributes: $attrs")

counter.add(1, attrs)
}
}
.onResponseBody { (ep, _) =>
m.eval {
val attrs = asOpenTelemetryAttributes(labels, ep, req)
println(s"[DEBUG] Decrementing active requests with attributes: $attrs")
counter.add(-1, attrs)
}
}
.onEndpointRequest { ep => m.eval(counter.add(1, asOpenTelemetryAttributes(labels, ep, req))) }
.onResponseBody { (ep, _) => m.eval(counter.add(-1, asOpenTelemetryAttributes(labels, ep, req))) }
.onException { (ep, _) => m.eval(counter.add(-1, asOpenTelemetryAttributes(labels, ep, req))) }
}
}
Expand All @@ -119,11 +106,9 @@ object OpenTelemetryMetrics {
EndpointMetric()
.onResponseBody { (ep, res) =>
m.eval {
val otLabels: Attributes =
val otLabels =
merge(asOpenTelemetryAttributes(labels, ep, req), asOpenTelemetryAttributes(labels, Right(res), None))

println(s"[DEBUG] Incrementing total requests with labels: $otLabels")

counter.add(1, otLabels)
}
}
Expand All @@ -138,7 +123,6 @@ object OpenTelemetryMetrics {
}
)


def requestDuration[F[_]](meter: Meter, labels: MetricLabels): Metric[F, DoubleHistogram] =
Metric[F, DoubleHistogram](
meter
Expand All @@ -149,7 +133,7 @@ object OpenTelemetryMetrics {
onRequest = (req, recorder, m) =>
m.eval {
val requestStart = Instant.now()
def duration = Duration.between(requestStart, Instant.now()).toMillis.toDouble / 1000.0
def duration = Duration.between(requestStart, Instant.now()).toMillis.toDouble
EndpointMetric()
.onResponseHeaders { (ep, res) =>
m.eval {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ class OpenTelemetryMetricsTest extends AnyFlatSpec with Matchers {
"GET",
AttributeKey.stringKey("path"),
"/person",
AttributeKey.longKey("http.response.status_code").asInstanceOf[AttributeKey[Long]], // Explicitly cast to Long
200L
AttributeKey.stringKey("http.response.status_code"),
"200"
) && dp.getValue == 2 =>
true
case dp
Expand All @@ -111,8 +111,8 @@ class OpenTelemetryMetricsTest extends AnyFlatSpec with Matchers {
"GET",
AttributeKey.stringKey("path"),
"/person",
AttributeKey.longKey("http.response.status_code").asInstanceOf[AttributeKey[Long]], // Explicitly cast to Long
400L
AttributeKey.stringKey("http.response.status_code"),
"400"
) && dp.getValue == 2 =>
true
case _ => false
Expand Down Expand Up @@ -154,8 +154,10 @@ class OpenTelemetryMetricsTest extends AnyFlatSpec with Matchers {
"GET",
AttributeKey.stringKey("path"),
"/person",
AttributeKey.longKey("http.response.status_code").asInstanceOf[AttributeKey[Long]], // Explicitly cast to Long
200L
AttributeKey.stringKey("http.response.status_code"),
"200",
AttributeKey.stringKey("phase"),
"body"
)
)
}
Expand Down Expand Up @@ -209,8 +211,8 @@ class OpenTelemetryMetricsTest extends AnyFlatSpec with Matchers {
"GET",
AttributeKey.stringKey("path"),
"/person",
AttributeKey.longKey("http.response.status_code").asInstanceOf[AttributeKey[Long]], // Explicitly cast to Long
500L
AttributeKey.stringKey("http.response.status_code"),
"500"
)
point.getValue shouldBe 1
}
Expand Down
19 changes: 19 additions & 0 deletions server/core/src/main/scala/sttp/tapir/server/metrics/Metric.scala
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,22 @@ object MetricLabels {
)
)
}

object OTELMetricLabels {

/** Labels request by path and http.request.method, response by http.response.status_code */
lazy val Default: MetricLabels = MetricLabels(
forRequest = List(
"http.request.method" -> { case (_, req) => req.method.method },
"path" -> { case (ep, _) => ep.showPathTemplate(showQueryParam = None) }
),
forResponse = List(
"http.response.status_code" -> {
// OpenTelemetry-compliant
case Right(r) => r.code.code.toString
// Default to 500 for exceptions
case Left(_) => "500"
}
)
)
}

0 comments on commit 453389b

Please sign in to comment.