Skip to content

Commit c5c6eb5

Browse files
authored
Merge pull request #168 from dj707chen/customMetricsOps
Update to use CustomMetricsOps
2 parents 54ff150 + 7f83b41 commit c5c6eb5

6 files changed

Lines changed: 312 additions & 212 deletions

File tree

build.sbt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
ThisBuild / tlBaseVersion := "0.24"
1+
ThisBuild / tlBaseVersion := "0.25"
22
ThisBuild / developers := List(
33
tlGitHubDev("rossabaker", "Ross A. Baker")
44
)
@@ -10,7 +10,7 @@ ThisBuild / startYear := Some(2018)
1010

1111
lazy val root = project.in(file(".")).aggregate(prometheusMetrics).enablePlugins(NoPublishPlugin)
1212

13-
val http4sVersion = "0.23.27"
13+
val http4sVersion = "0.23.28"
1414
val prometheusVersion = "0.16.0"
1515
val munitVersion = "1.0.0"
1616
val munitCatsEffectVersion = "2.0.0"

prometheus-metrics/src/main/scala/org/http4s/metrics/prometheus/Prometheus.scala

Lines changed: 62 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@ import cats.data.NonEmptyList
2020
import cats.effect.Resource
2121
import cats.effect.Sync
2222
import cats.syntax.all._
23+
import io.prometheus.client.CollectorRegistry
2324
import io.prometheus.client._
2425
import org.http4s.Method
2526
import org.http4s.Status
27+
import org.http4s.metrics.CustomLabels
28+
import org.http4s.metrics.CustomMetricsOps
29+
import org.http4s.metrics.EmptyCustomLabels
2630
import org.http4s.metrics.MetricsOps
2731
import org.http4s.metrics.TerminationType
2832
import org.http4s.metrics.TerminationType.Abnormal
@@ -31,6 +35,7 @@ import org.http4s.metrics.TerminationType.Error
3135
import org.http4s.metrics.TerminationType.Timeout
3236
import org.http4s.metrics.prometheus.Prometheus.registerCollector
3337
import org.http4s.metrics.prometheus.Prometheus.toFlatArray
38+
import org.http4s.util.SizedSeq
3439

3540
/** [[MetricsOps]] algebra capable of recording Prometheus metrics
3641
*
@@ -88,22 +93,19 @@ final class Prometheus[F[_]: Sync] private (
8893
private val prefix: String,
8994
private val registry: CollectorRegistry,
9095
private val sampleExemplar: F[Option[Map[String, String]]],
91-
private val customLabelsAndValues: List[(String, String)],
9296
private val responseDurationSecondsHistogramBuckets: NonEmptyList[Double],
9397
) { self =>
9498
private def copy(
9599
prefix: String = self.prefix,
96100
registry: CollectorRegistry = self.registry,
97101
sampleExemplar: F[Option[Map[String, String]]] = self.sampleExemplar,
98-
customLabelsAndValues: List[(String, String)] = self.customLabelsAndValues,
99102
responseDurationSecondsHistogramBuckets: NonEmptyList[Double] =
100103
self.responseDurationSecondsHistogramBuckets,
101104
): Prometheus[F] =
102105
new Prometheus[F](
103106
prefix = prefix,
104107
registry = registry,
105108
sampleExemplar = sampleExemplar,
106-
customLabelsAndValues = customLabelsAndValues,
107109
responseDurationSecondsHistogramBuckets = responseDurationSecondsHistogramBuckets,
108110
)
109111

@@ -113,41 +115,52 @@ final class Prometheus[F[_]: Sync] private (
113115
def withSampleExemplar(sampleExemplar: F[Option[Map[String, String]]]): Prometheus[F] =
114116
copy(sampleExemplar = sampleExemplar)
115117

116-
def withCustomLabelsAndValues(
117-
customLabelsAndValues: List[(String, String)]
118-
): Prometheus[F] = copy(customLabelsAndValues = customLabelsAndValues)
119-
120118
def withResponseDurationSecondsHistogramBuckets(
121119
responseDurationSecondsHistogramBuckets: NonEmptyList[Double]
122120
): Prometheus[F] =
123121
copy(responseDurationSecondsHistogramBuckets = responseDurationSecondsHistogramBuckets)
124122

125123
/** Build a [[MetricsOps]] that supports Prometheus metrics */
126-
def build: Resource[F, MetricsOps[F]] = createMetricsCollection.map(createMetricsOps)
124+
def build: Resource[F, MetricsOps[F]] = buildCustomMetricsOps(EmptyCustomLabels())
125+
126+
def buildCustomMetricsOps[SL <: SizedSeq[String]](
127+
pCustomLabels: CustomLabels[SL]
128+
): Resource[F, CustomMetricsOps[F, SL]] =
129+
createMetricsCollection(pCustomLabels).map(createMetricsOps(pCustomLabels))
127130

128-
private def createMetricsOps(metrics: MetricsCollection): MetricsOps[F] = {
129-
val customLabelValues: List[String] = customLabelsAndValues.map(_._2)
131+
private def createMetricsOps[SL <: SizedSeq[String]](pCustomLabels: CustomLabels[SL])(
132+
metrics: MetricsCollection[SL]
133+
): CustomMetricsOps[F, SL] = {
130134
val exemplarLabels: F[Option[Array[String]]] = sampleExemplar.map(_.map(toFlatArray))
131135

132-
new MetricsOps[F] {
133-
override def increaseActiveRequests(classifier: Option[String]): F[Unit] =
136+
new CustomMetricsOps[F, SL] {
137+
override def definingCustomLabels: CustomLabels[SL] = pCustomLabels
138+
139+
override def increaseActiveRequests(
140+
classifier: Option[String],
141+
customLabelValues: SL,
142+
): F[Unit] =
134143
Sync[F].delay {
135144
metrics.activeRequests
136-
.labels(label(classifier) +: customLabelValues: _*)
145+
.labels(label(classifier) +: customLabelValues.toSeq: _*)
137146
.inc()
138147
}
139148

140-
override def decreaseActiveRequests(classifier: Option[String]): F[Unit] =
149+
override def decreaseActiveRequests(
150+
classifier: Option[String],
151+
customLabelValues: SL,
152+
): F[Unit] =
141153
Sync[F].delay {
142154
metrics.activeRequests
143-
.labels(label(classifier) +: customLabelValues: _*)
155+
.labels(label(classifier) +: customLabelValues.toSeq: _*)
144156
.dec()
145157
}
146158

147159
override def recordHeadersTime(
148160
method: Method,
149161
elapsed: Long,
150162
classifier: Option[String],
163+
customLabelValues: SL,
151164
): F[Unit] =
152165
exemplarLabels.flatMap { exemplarOpt =>
153166
Sync[F].delay {
@@ -156,7 +169,7 @@ final class Prometheus[F[_]: Sync] private (
156169
label(classifier) +:
157170
reportMethod(method) +:
158171
Phase.report(Phase.Headers) +:
159-
customLabelValues: _*
172+
customLabelValues.toSeq: _*
160173
)
161174
.observeWithExemplar(
162175
SimpleTimer.elapsedSecondsFromNanos(0, elapsed),
@@ -170,6 +183,7 @@ final class Prometheus[F[_]: Sync] private (
170183
status: Status,
171184
elapsed: Long,
172185
classifier: Option[String],
186+
customLabelValues: SL,
173187
): F[Unit] =
174188
exemplarLabels.flatMap { exemplarOpt =>
175189
Sync[F].delay {
@@ -178,7 +192,7 @@ final class Prometheus[F[_]: Sync] private (
178192
label(classifier) +:
179193
reportMethod(method) +:
180194
Phase.report(Phase.Body) +:
181-
customLabelValues: _*
195+
customLabelValues.toSeq: _*
182196
)
183197
.observeWithExemplar(
184198
SimpleTimer.elapsedSecondsFromNanos(0, elapsed),
@@ -189,7 +203,7 @@ final class Prometheus[F[_]: Sync] private (
189203
label(classifier) +:
190204
reportMethod(method) +:
191205
reportStatus(status) +:
192-
customLabelValues: _*
206+
customLabelValues.toSeq: _*
193207
)
194208
.incWithExemplar(exemplarOpt.orNull: _*)
195209
}
@@ -199,23 +213,28 @@ final class Prometheus[F[_]: Sync] private (
199213
elapsed: Long,
200214
terminationType: TerminationType,
201215
classifier: Option[String],
216+
customLabelValues: SL,
202217
): F[Unit] =
203218
terminationType match {
204-
case Abnormal(e) => recordAbnormal(elapsed, classifier, e)
205-
case Error(e) => recordError(elapsed, classifier, e)
206-
case Canceled => recordCanceled(elapsed, classifier)
207-
case Timeout => recordTimeout(elapsed, classifier)
219+
case Abnormal(e) => recordAbnormal(elapsed, classifier, customLabelValues, e)
220+
case Error(e) => recordError(elapsed, classifier, customLabelValues, e)
221+
case Canceled => recordCanceled(elapsed, classifier, customLabelValues)
222+
case Timeout => recordTimeout(elapsed, classifier, customLabelValues)
208223
}
209224

210-
private def recordCanceled(elapsed: Long, classifier: Option[String]): F[Unit] =
225+
private def recordCanceled(
226+
elapsed: Long,
227+
classifier: Option[String],
228+
customLabelValues: SL,
229+
): F[Unit] =
211230
exemplarLabels.flatMap { exemplarOpt =>
212231
Sync[F].delay {
213232
metrics.abnormalTerminations
214233
.labels(
215234
label(classifier) +:
216235
AbnormalTermination.report(AbnormalTermination.Canceled) +:
217236
label(Option.empty) +:
218-
customLabelValues: _*
237+
customLabelValues.toSeq: _*
219238
)
220239
.observeWithExemplar(
221240
SimpleTimer.elapsedSecondsFromNanos(0, elapsed),
@@ -227,6 +246,7 @@ final class Prometheus[F[_]: Sync] private (
227246
private def recordAbnormal(
228247
elapsed: Long,
229248
classifier: Option[String],
249+
customLabelValues: SL,
230250
cause: Throwable,
231251
): F[Unit] =
232252
exemplarLabels.flatMap { exemplarOpt =>
@@ -236,7 +256,7 @@ final class Prometheus[F[_]: Sync] private (
236256
label(classifier) +:
237257
AbnormalTermination.report(AbnormalTermination.Abnormal) +:
238258
label(Option(cause.getClass.getName)) +:
239-
customLabelValues: _*
259+
customLabelValues.toSeq: _*
240260
)
241261
.observeWithExemplar(
242262
SimpleTimer.elapsedSecondsFromNanos(0, elapsed),
@@ -248,6 +268,7 @@ final class Prometheus[F[_]: Sync] private (
248268
private def recordError(
249269
elapsed: Long,
250270
classifier: Option[String],
271+
customLabelValues: SL,
251272
cause: Throwable,
252273
): F[Unit] =
253274
exemplarLabels.flatMap { exemplarOpt =>
@@ -257,7 +278,7 @@ final class Prometheus[F[_]: Sync] private (
257278
label(classifier) +:
258279
AbnormalTermination.report(AbnormalTermination.Error) +:
259280
label(Option(cause.getClass.getName)) +:
260-
customLabelValues: _*
281+
customLabelValues.toSeq: _*
261282
)
262283
.observeWithExemplar(
263284
SimpleTimer.elapsedSecondsFromNanos(0, elapsed),
@@ -266,15 +287,19 @@ final class Prometheus[F[_]: Sync] private (
266287
}
267288
}
268289

269-
private def recordTimeout(elapsed: Long, classifier: Option[String]): F[Unit] =
290+
private def recordTimeout(
291+
elapsed: Long,
292+
classifier: Option[String],
293+
customLabelValues: SL,
294+
): F[Unit] =
270295
exemplarLabels.flatMap { exemplarOpt =>
271296
Sync[F].delay {
272297
metrics.abnormalTerminations
273298
.labels(
274299
label(classifier) +:
275300
AbnormalTermination.report(AbnormalTermination.Timeout) +:
276301
label(Option.empty) +:
277-
customLabelValues: _*
302+
customLabelValues.toSeq: _*
278303
)
279304
.observeWithExemplar(
280305
SimpleTimer.elapsedSecondsFromNanos(0, elapsed),
@@ -311,16 +336,18 @@ final class Prometheus[F[_]: Sync] private (
311336
}
312337
}
313338

314-
private def createMetricsCollection: Resource[F, MetricsCollection] = {
315-
val customLabels: List[String] = customLabelsAndValues.map(_._1)
339+
private def createMetricsCollection[SL <: SizedSeq[String]](
340+
pCustomLabels: CustomLabels[SL]
341+
): Resource[F, MetricsCollection[SL]] = {
342+
val customLabels = pCustomLabels.labels
316343

317344
val responseDuration: Resource[F, Histogram] = registerCollector(
318345
Histogram
319346
.build()
320347
.buckets(responseDurationSecondsHistogramBuckets.toList: _*)
321348
.name(prefix + "_" + "response_duration_seconds")
322349
.help("Response Duration in seconds.")
323-
.labelNames("classifier" +: "method" +: "phase" +: customLabels: _*)
350+
.labelNames("classifier" +: "method" +: "phase" +: customLabels.toSeq: _*)
324351
.create(),
325352
registry,
326353
)
@@ -330,7 +357,7 @@ final class Prometheus[F[_]: Sync] private (
330357
.build()
331358
.name(prefix + "_" + "active_request_count")
332359
.help("Total Active Requests.")
333-
.labelNames("classifier" +: customLabels: _*)
360+
.labelNames("classifier" +: customLabels.toSeq: _*)
334361
.create(),
335362
registry,
336363
)
@@ -340,7 +367,7 @@ final class Prometheus[F[_]: Sync] private (
340367
.build()
341368
.name(prefix + "_" + "request_count")
342369
.help("Total Requests.")
343-
.labelNames("classifier" +: "method" +: "status" +: customLabels: _*)
370+
.labelNames("classifier" +: "method" +: "status" +: customLabels.toSeq: _*)
344371
.create(),
345372
registry,
346373
)
@@ -350,7 +377,7 @@ final class Prometheus[F[_]: Sync] private (
350377
.build()
351378
.name(prefix + "_" + "abnormal_terminations")
352379
.help("Total Abnormal Terminations.")
353-
.labelNames("classifier" +: "termination_type" +: "cause" +: customLabels: _*)
380+
.labelNames("classifier" +: "termination_type" +: "cause" +: customLabels.toSeq: _*)
354381
.create(),
355382
registry,
356383
)
@@ -432,13 +459,12 @@ object Prometheus {
432459
prefix = "org_http4s_server",
433460
registry = registry,
434461
sampleExemplar = Option.empty[Map[String, String]].pure,
435-
customLabelsAndValues = List.empty,
436462
responseDurationSecondsHistogramBuckets = DefaultHistogramBuckets,
437463
)
438464

439465
}
440466

441-
final case class MetricsCollection(
467+
final case class MetricsCollection[SL <: SizedSeq[String]](
442468
responseDuration: Histogram,
443469
activeRequests: Gauge,
444470
requests: Counter,

0 commit comments

Comments
 (0)