- tagless final api via cats
- improved abstraction for matching labels names and label values
- resource safety
- detaching metrics backend - prometheus dependency is one of the backend implementations
let's declare interface for metrics, that seems reasonable for http client
trait HttpMetrics[F[_]] {
def latency(client: String, method: String, resource: String, duration: FiniteDuration): F[Unit]
def count(client: String, method: String, resource: String, success: Boolean): F[Unit]
def enqueue(client: String): F[Unit]
def dequeue(client: String): F[Unit]
}now let's implement HttpMetrics using CollectorRegistry
import com.evolutiongaming.smetrics.MetricsHelper.*
import com.evolutiongaming.smetrics.{CollectorRegistry, LabelNames, Quantile, Quantiles}
def httpMetrics[F[_]: Monad](collectorRegistry: CollectorRegistry[F]): Resource[F, HttpMetrics[F]] = {
for {
latencySummary <- collectorRegistry.summary(
"http_client_latency",
"Latency of HTTP requests processing in seconds",
Quantiles(
Quantile(value = 0.5, error = 0.05),
Quantile(value = 0.9, error = 0.05),
Quantile(value = 0.99, error = 0.005)),
LabelNames("client_name", "method", "resource"))
resultCounter <- collectorRegistry.counter(
"http_client_response_result",
"Status of HTTP responses",
LabelNames("client_name", "method", "resource", "result"))
queueGauge <- collectorRegistry.gauge(
"http_client_queue",
"Queue of incoming http calls",
LabelNames("client_name"))
} yield {
new HttpMetrics[F] {
def latency(client: String, method: String, resource: String, duration: FiniteDuration) = {
latencySummary
.labels(client, method, resource)
.observe(duration.toNanos.nanosToSeconds)
}
def count(client: String, method: String, resource: String, success: Boolean) = {
resultCounter
.labels(client, method, resource, if (success) "success" else "error")
.inc()
}
def enqueue(client: String) = {
queueGauge
.labels(client)
.inc()
}
def dequeue(client: String) = {
queueGauge
.labels(client)
.dec()
}
}
}
}So you can see, httpMetrics function requires instance of CollectorRegistry.
Right now there are two options available:
CollectorRegistry.empty- often used in testsCollectorRegistryPrometheus- which will createsmetrics.CollectorRegistryout ofio.prometheus.CollectorRegistryusing prometheus/client_java under the hood
Return type is Resource[F, _] rather than F[_] because most often underlying metrics implementation upon a call registers your metrics with shared registry.
Hence release hook of Resource being used in order to de-register particular metrics from shared registry.
addSbtPlugin("com.evolution" % "sbt-artifactory-plugin" % "0.0.2")
libraryDependencies += "com.evolutiongaming" %% "smetrics" % "0.3.1"
libraryDependencies += "com.evolutiongaming" %% "smetrics-prometheus" % "0.3.1"For prometheus version 1.x.x+ use prometheus_v1 module:
libraryDependencies += "com.evolutiongaming" %% "smetrics-prometheus-v1" % "0.3.1"For sttp3 use smetrics-sttp3 module:
libraryDependencies += "com.evolutiongaming" %% "smetrics-sttp3" % "x.y.z"