diff --git a/core/src/main/scala-2/prometheus4cats/internal/ExemplarLabelNameFromStringLiteral.scala b/core/src/main/scala-2/prometheus4cats/internal/ExemplarLabelNameFromStringLiteral.scala new file mode 100644 index 00000000..c2d7448c --- /dev/null +++ b/core/src/main/scala-2/prometheus4cats/internal/ExemplarLabelNameFromStringLiteral.scala @@ -0,0 +1,46 @@ +/* + * Copyright 2022 Permutive + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package prometheus4cats.internal + +import prometheus4cats.Exemplar + +import scala.reflect.macros.blackbox + +private[prometheus4cats] trait ExemplarLabelNameFromStringLiteral { + + def apply(t: String): Exemplar.LabelName = + macro ExemplarLabelNameMacros.fromStringLiteral + + implicit def fromStringLiteral(t: String): Exemplar.LabelName = + macro ExemplarLabelNameMacros.fromStringLiteral + +} + +private[prometheus4cats] class ExemplarLabelNameMacros(val c: blackbox.Context) extends MacroUtils { + + def fromStringLiteral(t: c.Expr[String]): c.Expr[Exemplar.LabelName] = { + val string: String = literal(t, or = "Exemplar.LabelName.from({string})") + + Exemplar.LabelName + .from(string) + .fold( + abort, + _ => c.universe.reify(Exemplar.LabelName.from(t.splice).toOption.get) + ) + } + +} diff --git a/core/src/main/scala-3/prometheus4cats/internal/ExemplarLabelNameFromStringLiteral.scala b/core/src/main/scala-3/prometheus4cats/internal/ExemplarLabelNameFromStringLiteral.scala new file mode 100644 index 00000000..573dca0d --- /dev/null +++ b/core/src/main/scala-3/prometheus4cats/internal/ExemplarLabelNameFromStringLiteral.scala @@ -0,0 +1,52 @@ +/* + * Copyright 2022 Permutive + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package prometheus4cats.internal + +import prometheus4cats._ + +import scala.quoted.* + +private[prometheus4cats] trait ExemplarLabelNameFromStringLiteral { + + inline def apply(inline t: String): Exemplar.LabelName = ${ + ExemplarLabelNameFromStringLiteral.nameLiteral('t) + } + + implicit inline def fromStringLiteral(inline t: String): Exemplar.LabelName = ${ + ExemplarLabelNameFromStringLiteral.nameLiteral('t) + } + +} + +private[prometheus4cats] object ExemplarLabelNameFromStringLiteral extends MacroUtils { + def nameLiteral(s: Expr[String])(using q: Quotes): Expr[Exemplar.LabelName] = + s.value match { + case Some(string) => + Exemplar.LabelName + .from(string) + .fold( + error, + _ => + '{ + Exemplar.LabelName.from(${ Expr(string) }).toOption.get + } + ) + case None => + abort("Exemplar.LabelName.from") + '{ ??? } + } +} diff --git a/core/src/main/scala/prometheus4cats/Exemplar.scala b/core/src/main/scala/prometheus4cats/Exemplar.scala new file mode 100644 index 00000000..d3465fbc --- /dev/null +++ b/core/src/main/scala/prometheus4cats/Exemplar.scala @@ -0,0 +1,76 @@ +/* + * Copyright 2022 Permutive + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package prometheus4cats + +import cats.Applicative +import cats.syntax.show._ +import prometheus4cats.internal.ExemplarLabelNameFromStringLiteral + +import java.util.regex.Pattern +import scala.collection.immutable.SortedMap + +/** A typeclass to provide exemplars to counters and histograms, which may be used by [[MetricRegistry]] + * implementations. + */ +trait Exemplar[F[_]] { + def get: F[Option[Exemplar.Labels]] +} + +object Exemplar { + def apply[F[_]: Exemplar]: Exemplar[F] = implicitly + + implicit def noop[F[_]: Applicative]: Exemplar[F] = new Exemplar[F] { + override def get: F[Option[Labels]] = Applicative[F].pure(None) + } + + /** Refined value class for an exemplar label name that has been parsed from a string + */ + final class LabelName private (val value: String) extends AnyVal with internal.Refined.Value[String] { + override def toString: String = s"""Exemplar.LabelName("$value")""" + } + + object LabelName extends internal.Refined.StringRegexRefinement[LabelName] with ExemplarLabelNameFromStringLiteral { + protected val regex: Pattern = "^[a-zA-Z_][a-zA-Z_0-9]*$".r.pattern + + implicit val ordering: Ordering[LabelName] = catsInstances.toOrdering + + override protected def make(a: String): LabelName = new LabelName(a) + } + + /** Refined value class for a set of exemplar labels. + * + * Validation will fail if the labels are empty or if the length of all the label names and values does not exceed + * 128 UTF-8 characters + */ + final class Labels private (val value: SortedMap[LabelName, String]) + extends AnyVal + with internal.Refined.Value[SortedMap[LabelName, String]] { + override def toString: String = s"""Exemplar.Labels("${value.show}")""" + } + + object Labels extends internal.Refined[SortedMap[LabelName, String], Labels] { + def fromMap(a: Map[LabelName, String]): Either[String, Labels] = from(SortedMap.from(a)) + + override protected def make(a: SortedMap[LabelName, String]): Labels = new Labels(a) + + override protected def test(a: SortedMap[LabelName, String]): Boolean = + a.nonEmpty && a.map { case (k, v) => s"${k.value}$v".length }.sum <= 128 + + override protected def nonMatchMessage(a: SortedMap[LabelName, String]): String = + "exemplar labels must not be empty and the combined length of the label names and values must not exceed 128 UTF-8 characters" + } +} diff --git a/docs/implementations/java.md b/docs/implementations/java.md index cc4f03c1..773ecef7 100644 --- a/docs/implementations/java.md +++ b/docs/implementations/java.md @@ -3,6 +3,8 @@ The Java registry implements both [`MetricRegistry`] and [`CallbackRegistry`], wrapping the [Prometheus Java library]. This provides interoperability with anything that depends on the Java library. +> ℹ️ As of version `1.1.0` of Prometheus4Cats, the Java registry now supports [Exemplars](../interface/exemplars.md) + > ℹ️ The Java Registry does add a runtime constraint that go beyond constraints that Prometheus itself imposes: > You cannot have two metrics of the same name with different labels. > [This issue](https://github.com/prometheus/client_java/issues/696) describes the problem. diff --git a/docs/interface/exemplars.md b/docs/interface/exemplars.md new file mode 100644 index 00000000..654b7b34 --- /dev/null +++ b/docs/interface/exemplars.md @@ -0,0 +1,8 @@ +# Exemplars + +Exemplars provide a way of linking trace information to metrics. For a detailed explainer see the +[Grafana labs introduction to exemplars](https://grafana.com/docs/grafana/latest/fundamentals/exemplars/). + +Exemplars are implemented in Prometheus4Cats using the `Exemplar` typeclass, which returns an optional map of exemplar +labels. You can provide your own implementation for this, but beware that your label names and values can't be any +longer than 128 UTF-8 characters. \ No newline at end of file diff --git a/java/src/main/scala/prometheus4cats/javasimpleclient/JavaMetricRegistry.scala b/java/src/main/scala/prometheus4cats/javasimpleclient/JavaMetricRegistry.scala index 45fc1a95..4d6188ea 100644 --- a/java/src/main/scala/prometheus4cats/javasimpleclient/JavaMetricRegistry.scala +++ b/java/src/main/scala/prometheus4cats/javasimpleclient/JavaMetricRegistry.scala @@ -16,8 +16,6 @@ package prometheus4cats.javasimpleclient -import java.util - import alleycats.std.iterable._ import cats.data.{NonEmptyList, NonEmptySeq} import cats.effect.kernel._ @@ -46,6 +44,7 @@ import prometheus4cats.javasimpleclient.internal.{HistogramUtils, MetricCollecti import prometheus4cats.javasimpleclient.models.MetricType import prometheus4cats.util.{DoubleCallbackRegistry, DoubleMetricRegistry, NameUtils} +import java.util import scala.concurrent.TimeoutException import scala.concurrent.duration._ import scala.jdk.CollectionConverters._ @@ -68,12 +67,12 @@ class JavaMetricRegistry[F[_]: Async: Logger] private ( with DoubleCallbackRegistry[F] { override protected val F: Functor[F] = implicitly - private def counterName[A: Show](name: A) = name match { + protected def counterName[A: Show](name: A): String = name match { case counter: Counter.Name => counter.value.replace("_total", "") case _ => name.show } - private def configureBuilderOrRetrieve[A: Show, B <: SimpleCollector.Builder[B, C], C <: SimpleCollector[_]]( + protected def configureBuilderOrRetrieve[A: Show, B <: SimpleCollector.Builder[B, C], C <: SimpleCollector[_]]( builder: SimpleCollector.Builder[B, C], metricType: MetricType, metricPrefix: Option[Metric.Prefix], @@ -172,7 +171,13 @@ class JavaMetricRegistry[F[_]: Async: Logger] private ( 1.0, (d: Double) => Utils - .modifyMetric[F, Counter.Name, PCounter.Child](counter, name, commonLabelNames, commonLabelValues, _.inc(d)) + .modifyMetric[F, Counter.Name, PCounter.Child]( + counter, + name, + commonLabelNames, + commonLabelValues, + _.inc(d) + ) ) } } @@ -866,6 +871,274 @@ object JavaMetricRegistry { "Number of times each metric callback has been executed, with a status (success, error, timeout)" private val callbackCounterLabels = List("metric_name", "status") + class WithExemplars[F[_]: Async: Logger: Exemplar] private[JavaMetricRegistry] ( + private val registry: CollectorRegistry, + private val ref: Ref[F, State], + private val callbackState: Ref[F, CallbackState[F]], + private val callbackTimeoutState: Ref[F, Set[String]], + private val callbackErrorState: Ref[F, Set[String]], + private val singleCallbackErrorState: Ref[F, (Set[String], Set[String])], + private val callbackCounter: PCounter, + private val singleCallbackCounter: PCounter, + private val metricCollectionCollector: MetricCollectionProcessor[F], + private val sem: Semaphore[F], + private val dispatcher: Dispatcher[F], + private val callbackTimeout: FiniteDuration, + private val singleCallbackTimeout: FiniteDuration + ) extends JavaMetricRegistry[F]( + registry, + ref, + callbackState, + callbackTimeoutState, + callbackErrorState, + singleCallbackErrorState, + callbackCounter, + singleCallbackCounter, + metricCollectionCollector, + sem, + dispatcher, + callbackTimeout, + singleCallbackTimeout + ) { + + private def transformExemplarLabels(labels: Exemplar.Labels): util.Map[String, String] = labels.value.map { + case (k, v) => k.value -> v + }.asJava + + override def createAndRegisterDoubleCounter( + prefix: Option[Metric.Prefix], + name: Counter.Name, + help: Metric.Help, + commonLabels: Metric.CommonLabels + ): Resource[F, Counter[F, Double]] = { + lazy val commonLabelNames = commonLabels.value.keys.toIndexedSeq + lazy val commonLabelValues = commonLabels.value.values.toIndexedSeq + + configureBuilderOrRetrieve( + PCounter.build().withExemplars(), + MetricType.Counter, + prefix, + name, + help, + commonLabels.value.keys.toIndexedSeq + ).map { counter => + Counter.make( + 1.0, + (d: Double) => + Exemplar[F].get.flatMap { ex => + Utils + .modifyMetric[F, Counter.Name, PCounter.Child]( + counter, + name, + commonLabelNames, + commonLabelValues, + c => ex.fold(c.inc(d))(e => c.incWithExemplar(d, transformExemplarLabels(e))) + ) + } + ) + } + } + + override def createAndRegisterLabelledDoubleCounter[A]( + prefix: Option[Metric.Prefix], + name: Counter.Name, + help: Metric.Help, + commonLabels: Metric.CommonLabels, + labelNames: IndexedSeq[Label.Name] + )(f: A => IndexedSeq[String]): Resource[F, Counter.Labelled[F, Double, A]] = { + val commonLabelNames = commonLabels.value.keys.toIndexedSeq + val commonLabelValues = commonLabels.value.values.toIndexedSeq + + configureBuilderOrRetrieve( + PCounter.build().withExemplars(), + MetricType.Counter, + prefix, + name, + help, + labelNames ++ commonLabels.value.keys.toIndexedSeq + ).map { counter => + Counter.Labelled.make( + 1.0, + (d: Double, labels: A) => + Exemplar[F].get.flatMap { ex => + Utils.modifyMetric[F, Counter.Name, PCounter.Child]( + counter, + name, + labelNames ++ commonLabelNames, + f(labels) ++ commonLabelValues, + c => ex.fold(c.inc(d))(e => c.incWithExemplar(d, transformExemplarLabels(e))) + ) + } + ) + } + } + + override def createAndRegisterDoubleHistogram( + prefix: Option[Metric.Prefix], + name: Histogram.Name, + help: Metric.Help, + commonLabels: Metric.CommonLabels, + buckets: NonEmptySeq[Double] + ): Resource[F, Histogram[F, Double]] = { + val commonLabelNames = commonLabels.value.keys.toIndexedSeq + val commonLabelValues = commonLabels.value.values.toIndexedSeq + + configureBuilderOrRetrieve( + PHistogram.build().withExemplars().buckets(buckets.toSeq: _*), + MetricType.Histogram, + prefix, + name, + help, + commonLabels.value.keys.toIndexedSeq + ).map { histogram => + Histogram.make(d => + Exemplar[F].get.flatMap { ex => + Utils.modifyMetric[F, Histogram.Name, PHistogram.Child]( + histogram, + name, + commonLabelNames, + commonLabelValues, + h => ex.fold(h.observe(d))(e => h.observeWithExemplar(d, transformExemplarLabels(e))) + ) + } + ) + } + } + + override def createAndRegisterLabelledDoubleHistogram[A]( + prefix: Option[Metric.Prefix], + name: Histogram.Name, + help: Metric.Help, + commonLabels: Metric.CommonLabels, + labelNames: IndexedSeq[Label.Name], + buckets: NonEmptySeq[Double] + )(f: A => IndexedSeq[String]): Resource[F, Histogram.Labelled[F, Double, A]] = { + val commonLabelNames = commonLabels.value.keys.toIndexedSeq + val commonLabelValues = commonLabels.value.values.toIndexedSeq + + configureBuilderOrRetrieve( + PHistogram.build().withExemplars().buckets(buckets.toSeq: _*), + MetricType.Histogram, + prefix, + name, + help, + labelNames ++ commonLabelNames + ).map { histogram => + Histogram.Labelled.make[F, Double, A](_observe = { (d: Double, labels: A) => + Exemplar[F].get.flatMap { ex => + Utils.modifyMetric[F, Histogram.Name, PHistogram.Child]( + histogram, + name, + labelNames ++ commonLabelNames, + f(labels) ++ commonLabelValues, + h => ex.fold(h.observe(d))(e => h.observeWithExemplar(d, transformExemplarLabels(e))) + ) + } + }) + } + } + } + + sealed abstract class Builder( + val promRegistry: CollectorRegistry, + val callbackTimeout: FiniteDuration, + val callbackCollectionTimeout: FiniteDuration + ) { + private def copy( + promRegistry: CollectorRegistry = promRegistry, + callbackTimeout: FiniteDuration = callbackTimeout, + callbackCollectionTimeout: FiniteDuration = callbackCollectionTimeout + ): Builder = new Builder(promRegistry, callbackTimeout, callbackCollectionTimeout) {} + + def withRegistry(promRegistry: CollectorRegistry): Builder = copy(promRegistry = promRegistry) + def withCallbackTimeout(callbackTimeout: FiniteDuration): Builder = + copy(callbackTimeout = callbackTimeout) + def withCallbackCollectionTimeout(callbackCollectionTimeout: FiniteDuration): Builder = + copy(callbackCollectionTimeout = callbackCollectionTimeout) + + def build[F[_]: Async: Logger: Exemplar]: Resource[F, JavaMetricRegistry.WithExemplars[F]] = + Dispatcher.sequential[F].flatMap { dis => + val callbacksCounter = + PCounter + .build(callbacksCounterName, callbackCounterHelp) + .labelNames(callbackCounterLabels: _*) + .create() + + val singleCallbackCounter = + PCounter + .build(callbackCounterName, callbacksCounterHelp) + .labelNames(callbackCounterLabels: _*) + .create() + + val acquire = for { + ref <- Ref.of[F, State](Map.empty) + metricsGauge = makeMetricsGauge(dis, ref) + _ <- Sync[F].delay(promRegistry.register(metricsGauge)) + callbackState <- Ref.of[F, CallbackState[F]](Map.empty) + callbacksGauge = makeCallbacksGauge(dis, callbackState) + _ <- Sync[F].delay(promRegistry.register(callbacksGauge)) + callbackTimeoutState <- Ref.of[F, Set[String]](Set.empty) + callbackErrorState <- Ref.of[F, Set[String]](Set.empty) + singleCallbacksErrorState <- Ref.of[F, (Set[String], Set[String])]((Set.empty, Set.empty)) + _ <- Sync[F].delay(promRegistry.register(callbacksCounter)) + _ <- Sync[F].delay(promRegistry.register(singleCallbackCounter)) + sem <- Semaphore[F](1L) + metricCollectionProcessor <- MetricCollectionProcessor + .create( + ref, + callbackState, + dis, + callbackTimeout, + callbackCollectionTimeout, + promRegistry + ) + .allocated + } yield ( + ref, + metricsGauge, + callbacksGauge, + metricCollectionProcessor._2, + new JavaMetricRegistry.WithExemplars[F]( + promRegistry, + ref, + callbackState, + callbackTimeoutState, + callbackErrorState, + singleCallbacksErrorState, + callbacksCounter, + singleCallbackCounter, + metricCollectionProcessor._1, + sem, + dis, + callbackCollectionTimeout, + callbackTimeout + ) + ) + + Resource + .make(acquire) { case (ref, metricsGauge, callbacksGauge, procRelease, _) => + Utils.unregister(metricsGauge, promRegistry) >> Utils.unregister(callbacksGauge, promRegistry) >> Utils + .unregister(callbacksCounter, promRegistry) >> Utils + .unregister(singleCallbackCounter, promRegistry) >> procRelease >> + ref.get.flatMap { metrics => + if (metrics.nonEmpty) + metrics.values + .map(_._2) + .toList + .traverse_ { case (collector, _) => + Utils.unregister(collector, promRegistry) + } + else Applicative[F].unit + } + } + .map(_._5) + } + } + + object Builder { + def apply(): Builder = new Builder(CollectorRegistry.defaultRegistry, 250.millis, 1.second) {} + } + /** Create a metric registry using the default `io.prometheus.client.CollectorRegistry` * * Note that this registry implementation introduces a runtime constraint that requires each metric must have a @@ -879,15 +1152,12 @@ object JavaMetricRegistry { * how long the combined set of callbacks for a metric or metric collection should take to time out. This is for * **all callbacks for a metric** or **all callbacks returning a metric collection**. */ + @deprecated("Use JavaMetricRegistry.Builder instead", "1.1.0") def default[F[_]: Async: Logger]( callbackTimeout: FiniteDuration = 250.millis, callbackCollectionTimeout: FiniteDuration = 1.second ): Resource[F, JavaMetricRegistry[F]] = - fromSimpleClientRegistry( - CollectorRegistry.defaultRegistry, - callbackTimeout, - callbackCollectionTimeout - ) + Builder().withCallbackTimeout(callbackTimeout).withCallbackCollectionTimeout(callbackCollectionTimeout).build[F] /** Create a metric registry using the given `io.prometheus.client.CollectorRegistry` * @@ -904,86 +1174,16 @@ object JavaMetricRegistry { * how long the combined set of callbacks for a metric or metric collection should take to time out. This is for * **all callbacks for a metric** or **all callbacks returning a metric collection**. */ + @deprecated("Use JavaMetricRegistry.Builder instead", "1.1.0") def fromSimpleClientRegistry[F[_]: Async: Logger]( promRegistry: CollectorRegistry, callbackTimeout: FiniteDuration = 250.millis, callbackCollectionTimeout: FiniteDuration = 1.second - ): Resource[F, JavaMetricRegistry[F]] = Dispatcher.sequential[F].flatMap { dis => - val callbacksCounter = - PCounter - .build(callbacksCounterName, callbackCounterHelp) - .labelNames(callbackCounterLabels: _*) - .create() - - val singleCallbackCounter = - PCounter - .build(callbackCounterName, callbacksCounterHelp) - .labelNames(callbackCounterLabels: _*) - .create() - - val acquire = for { - ref <- Ref.of[F, State](Map.empty) - metricsGauge = makeMetricsGauge(dis, ref) - _ <- Sync[F].delay(promRegistry.register(metricsGauge)) - callbackState <- Ref.of[F, CallbackState[F]](Map.empty) - callbacksGauge = makeCallbacksGauge(dis, callbackState) - _ <- Sync[F].delay(promRegistry.register(callbacksGauge)) - callbackTimeoutState <- Ref.of[F, Set[String]](Set.empty) - callbackErrorState <- Ref.of[F, Set[String]](Set.empty) - singleCallbacksErrorState <- Ref.of[F, (Set[String], Set[String])]((Set.empty, Set.empty)) - _ <- Sync[F].delay(promRegistry.register(callbacksCounter)) - _ <- Sync[F].delay(promRegistry.register(singleCallbackCounter)) - sem <- Semaphore[F](1L) - metricCollectionProcessor <- MetricCollectionProcessor - .create( - ref, - callbackState, - dis, - callbackTimeout, - callbackCollectionTimeout, - promRegistry - ) - .allocated - } yield ( - ref, - metricsGauge, - callbacksGauge, - metricCollectionProcessor._2, - new JavaMetricRegistry[F]( - promRegistry, - ref, - callbackState, - callbackTimeoutState, - callbackErrorState, - singleCallbacksErrorState, - callbacksCounter, - singleCallbackCounter, - metricCollectionProcessor._1, - sem, - dis, - callbackCollectionTimeout, - callbackTimeout - ) - ) - - Resource - .make(acquire) { case (ref, metricsGauge, callbacksGauge, procRelease, _) => - Utils.unregister(metricsGauge, promRegistry) >> Utils.unregister(callbacksGauge, promRegistry) >> Utils - .unregister(callbacksCounter, promRegistry) >> Utils - .unregister(singleCallbackCounter, promRegistry) >> procRelease >> - ref.get.flatMap { metrics => - if (metrics.nonEmpty) - metrics.values - .map(_._2) - .toList - .traverse_ { case (collector, _) => - Utils.unregister(collector, promRegistry) - } - else Applicative[F].unit - } - } - .map(_._5) - } + ): Resource[F, JavaMetricRegistry[F]] = Builder() + .withRegistry(promRegistry) + .withCallbackTimeout(callbackTimeout) + .withCallbackCollectionTimeout(callbackCollectionTimeout) + .build[F] private def makeCallbacksGauge[F[_]: Monad](dis: Dispatcher[F], state: Ref[F, CallbackState[F]]) = new Collector { override def collect(): util.List[MetricFamilySamples] = dis.unsafeRunSync( diff --git a/java/src/test/scala/prometheus4cats/javasimpleclient/JavaMetricRegistrySuite.scala b/java/src/test/scala/prometheus4cats/javasimpleclient/JavaMetricRegistrySuite.scala index 6a9c64f8..a920fde3 100644 --- a/java/src/test/scala/prometheus4cats/javasimpleclient/JavaMetricRegistrySuite.scala +++ b/java/src/test/scala/prometheus4cats/javasimpleclient/JavaMetricRegistrySuite.scala @@ -46,10 +46,10 @@ class JavaMetricRegistrySuite override val stateResource: Resource[IO, CollectorRegistry] = Resource.eval(IO.delay(new CollectorRegistry())) override def metricRegistryResource(state: CollectorRegistry): Resource[IO, MetricRegistry[IO]] = - JavaMetricRegistry.fromSimpleClientRegistry[IO](state) + JavaMetricRegistry.Builder().withRegistry(state).build[IO] override def callbackRegistryResource(state: CollectorRegistry): Resource[IO, CallbackRegistry[IO]] = - JavaMetricRegistry.fromSimpleClientRegistry[IO](state, callbackTimeout = 100.millis) + JavaMetricRegistry.Builder().withRegistry(state).withCallbackTimeout(100.millis).build[IO] def getMetricValue[A: Show]( state: CollectorRegistry, @@ -212,7 +212,7 @@ class JavaMetricRegistrySuite labels: Set[Label.Name] ) => stateResource - .flatMap(JavaMetricRegistry.fromSimpleClientRegistry(_)) + .flatMap(JavaMetricRegistry.Builder().withRegistry(_).build) .use { reg => val metric = reg .createAndRegisterLabelledDoubleCounter[Map[Label.Name, String]]( @@ -257,7 +257,7 @@ class JavaMetricRegistrySuite labels: Set[Label.Name] ) => stateResource - .flatMap(JavaMetricRegistry.fromSimpleClientRegistry(_)) + .flatMap(JavaMetricRegistry.Builder().withRegistry(_).build) .use { reg => val metric = reg .createAndRegisterLabelledDoubleCounter[Map[Label.Name, String]](