diff --git a/.scala-steward.conf b/.scala-steward.conf new file mode 100644 index 0000000..4996240 --- /dev/null +++ b/.scala-steward.conf @@ -0,0 +1,3 @@ +pullRequests.grouping = [ + { name = "all", title = "Dependency updates", "filter" = [{"group" = "*"}] } +] diff --git a/README.md b/README.md new file mode 100644 index 0000000..eb6c636 --- /dev/null +++ b/README.md @@ -0,0 +1,93 @@ +# Prometheus4Cats Contrib + +This repository contains a series of integrations for [Prometheus4Cats] that instrument other libraries or metric +sources. Each integration is documented below: + +## Bigtable + +Google Bigtable [Java client instrumentation](https://cloud.google.com/bigtable/docs/client-side-metrics). +Uses a [Prometheus4Cats] callback registry to inspect the client metrics registered with OpenCensus. + +Use the [`BigtableOpenCensusMetrics`](bigtable/src/main/scala/prometheus4cats/bigtable/BigtableOpenCensusMetrics.scala) +smart constructor to enable metric collection on the Java client and registering them with a +[`MetricFactory.WithCallbacks`]. + +```sbt +"com.permutive" %% "prometheus4cats-contrib-google-cloud-bigtable" % "" +``` + +## Cats-Effect + +[Cats-Effect] metrics taken from JMX MBeans. This includes a +[CPU starvation](https://typelevel.org/cats-effect/docs/core/starvation-and-tuning) counter. + +Use the [`CatsEffectMBeans`](cats-effect/src/main/scala/prometheus4cats/catseffect/CatsEffectMBeans.scala) +smart constructor register callbacks for [Cats-Effect] MBeans with [`MetricFactory.WithCallbacks`]. + +```sbt +"com.permutive" %% "prometheus4cats-contrib-cats-effect" % "" +``` + +## FS2-Kafka + +[FS2-Kafka] integration that instruments consumers and producers. Uses a [Prometheus4Cats] callback registry to inspect +built-in [Kafka client metrics](https://docs.confluent.io/platform/current/kafka/monitoring.html#). + +Use the smart constructors in [`KafkaMetrics`](fs2-kafka/src/main/scala/prometheus4cats/fs2kafka/KafkaMetrics.scala) +to register callbacks for producers and consumers with a [`MetricFactory.WithCallbacks`]. + +```sbt +"com.permutive" %% "prometheus4cats-contrib-fs2-kafka" % "" +``` + +## Refreshable + +Instrumented implementation of [Refreshable]. Provides the following metrics: + +| Metric Name | Labels | Metric Type | Description | +|--------------------------------------|-----------------------------------|-------------|-------------------------------------------------------------------------------------------------------------| +| `refreshable_read_total` | `refreshable_name`, `value_state` | Counter | Number of times this Refreshable has been read, with a label denoting the state of the value | +| `refreshable_is_running` | `refreshable_name` | Gauge | Whether this Refreshable is running - `1` if true, `0` if false | +| `refreshable_retries_exhausted` | `refreshable_name` | Gauge | Whether retries have been exhausted for this Refreshable - `1` if true, `0` if false | +| `refreshable_refresh_failing` | `refreshable_name` | Gauge | Whether refresh is currently failing - `1` if true, `0` if false | +| `refreeshable_refresh_success_total` | `refreshable_name` | Counter | Number of times the refresh operation has succeeded | +| `refreshable_refresh_failure_total` | `refreshable_name` | Counter | Number of times refresh failed | +| `refreshable_status` | `refreshable_name`, `value_state` | Gauge | The current status of this Refreshable - a value of `1` against the label value indicates the current state | + +Use the [`InstrumentedRefreshable`](refreshable/src/main/scala/prometheus4cats/refreshable/InstrumentedRefreshable.scala) +smart constructor to instrument a given `Refreshable` when used with an instance of [`MetricFactory.WithCallbacks`]. + +```sbt +"com.permutive" %% "prometheus4cats-contrib-refreshable" % "" +``` + +## Trace4Cats + +Instrumented implementations of [Trace4Cats] interfaces. Provides the following metrics: + +| Interface | Metric Name | Labels | Metric Type | Description | +|-----------------|--------------------------------------|-------------------------------------------|-------------|----------------------------------------------------| +| `EntryPoint` | `trace4cats_entry_point_spans_total` | `span_kind`, `is_root`, `sample_decision` | Counter | Total number of spans created | +| `SpanCompleter` | `trace4cats_completer_spans_total` | `completer_name` | Counter | Total number of spans completed | +| `SpanCompleter` | `trace4cats_completer_complete_time` | `completer_name` | Histogram | Time it takes to complete a span in seconds | +| `SpanExporter` | `trace4cats_exporter_batches_total` | `exporter_name` | Counter | Total number of batches sent via this exporter | +| `SpanExporter` | `trace4cats_exporter_export_time` | `exporter_name` | Histogram | Time it takes to export a span batch in seconds | +| `SpanExporter` | `trace4cats_exporter_batch_size` | `exporter_name` | Histogram | Size distribution of batches sent by this exporter | + +Use the [`InstrumentedEntrypoint`](trace4cats/src/main/scala/prometheus4cats/trace4cats/InstrumentedEntrypoint.scala), +[`InstrumentedSpanCompleter`](trace4cats/src/main/scala/prometheus4cats/trace4cats/InstrumentedSpanCompleter.scala) and +[`InstrumentedSpanExporter`](trace4cats/src/main/scala/prometheus4cats/trace4cats/InstrumentedSpanExporter.scala) with +a [`MetricFactory`] to return instrumented wrappers of the underlying implementations. + +```sbt +"com.permutive" %% "prometheus4cats-contrib-trace4cats" % "" +``` + +[Cats-Effect]: https://typelevel.org/cats-effect +[FS2-Kafka]: https://fd4s.github.io/fs2-kafka/ +[Refreshable]: https://github.com/permutive-engineering/refreshable +[Trace4Cats]: https://github.com/trace4cats/trace4cats +[Prometheus4Cats]: https://github.com/permutive-engineering/prometheus4cats + +[`MetricFactory`]: https://permutive-engineering.github.io/prometheus4cats/interface/metric-factory.html +[`MetricFactory.WithCallbacks`]: https://permutive-engineering.github.io/prometheus4cats/interface/metric-factory.html diff --git a/build.sbt b/build.sbt index ee4dbba..5ad11f9 100644 --- a/build.sbt +++ b/build.sbt @@ -20,7 +20,7 @@ val Scala213 = "2.13.10" ThisBuild / crossScalaVersions := Seq("2.12.17", Scala213, "3.2.1") ThisBuild / scalaVersion := Scala213 // the default Scala -val Prometheus4Cats = "1.0.0-RC3" +val Prometheus4Cats = "1.0.0" val CollectionCompat = "2.9.0" diff --git a/fs2-kafka/src/test/scala/prometheus4cats/fs2kafka/KafkaMetricsSuite.scala b/fs2-kafka/src/test/scala/prometheus4cats/fs2kafka/KafkaMetricsSuite.scala index 7000525..93cca95 100644 --- a/fs2-kafka/src/test/scala/prometheus4cats/fs2kafka/KafkaMetricsSuite.scala +++ b/fs2-kafka/src/test/scala/prometheus4cats/fs2kafka/KafkaMetricsSuite.scala @@ -129,8 +129,7 @@ class KafkaMetricsSuite extends CatsEffectSuite with TestContainerForAll { Resource.eval(IO.delay(new CollectorRegistry())).flatMap { reg => JavaMetricRegistry .fromSimpleClientRegistry[IO]( - reg, - metricCollectionCallbackTimeout = 1.second + reg ) .map(MetricFactory.builder.build(_) -> reg) } diff --git a/refreshable/src/main/scala/prometheus4cats/refreshable/InstrumentedRefreshable.scala b/refreshable/src/main/scala/prometheus4cats/refreshable/InstrumentedRefreshable.scala index 8562973..ee14432 100644 --- a/refreshable/src/main/scala/prometheus4cats/refreshable/InstrumentedRefreshable.scala +++ b/refreshable/src/main/scala/prometheus4cats/refreshable/InstrumentedRefreshable.scala @@ -16,7 +16,8 @@ package prometheus4cats.refreshable -import cats.Applicative +import cats.data.NonEmptyList +import cats.{Applicative, Functor} import cats.effect.kernel.syntax.monadCancel._ import cats.effect.kernel.syntax.resource._ import cats.effect.kernel.syntax.spawn._ @@ -144,30 +145,27 @@ object InstrumentedRefreshable { ).tupled } - // TODO this might need a new release of prometheus4cats so multiple label values can be returned to represent when - // there are no errors or no cancellation, we'll have to see how this looks in prometheus - // FIXME multiple instances of this callback break prometheus4cats -// private def callback[F[_]: Functor, A]( -// name: String, -// refreshable: Refreshable[F, A], -// metricFactory: MetricFactory.WithCallbacks[F] -// ): Resource[F, Unit] = -// metricFactory -// .withPrefix(prefix) -// .gauge("status") -// .ofLong -// .help("The current status of this Refreshable") -// .label[String](refreshableLabelName) -// .label[CachedValue[A]]( -// "value_state", -// { -// case CachedValue.Success(_) => "success" -// case CachedValue.Error(_, _) => "error" -// case CachedValue.Cancelled(_) => "cancelled" -// } -// ) -// .callback(refreshable.get.map { v => (1, (name, v)) }) -// .build + private def callback[F[_]: Functor, A]( + name: String, + refreshable: Refreshable[F, A], + metricFactory: MetricFactory.WithCallbacks[F] + ): Resource[F, Unit] = + metricFactory + .withPrefix(prefix) + .gauge("status") + .ofLong + .help("The current status of this Refreshable") + .label[String](refreshableLabelName) + .label[CachedValue[A]]( + "value_state", + { + case CachedValue.Success(_) => "success" + case CachedValue.Error(_, _) => "error" + case CachedValue.Cancelled(_) => "cancelled" + } + ) + .callback(refreshable.get.map { v => NonEmptyList.one((1L, (name, v))) }) + .build private def metrics[F[_]: MonadCancelThrow, A]( name: String, @@ -268,7 +266,7 @@ object InstrumentedRefreshable { .onExhaustedRetries(onExhaustedRetries) .resource .evalTap(_ => runningGauge.set(true, name)) -// .flatTap(callback(name, _, metricFactory)) + .flatTap(callback(name, _, metricFactory)) .map { refreshable => new InstrumentedRefreshable[F, A]( refreshable, @@ -298,9 +296,7 @@ object InstrumentedRefreshable { refreshFailureCounter ) ) => -// callback(name, refreshable, metricFactory) -// .evalTap(_ => runningGauge.set(true, name)) - Resource + callback(name, refreshable, metricFactory) >> Resource .uncancelable((_: Poll[Resource[F, *]]) => Resource.make(runningGauge.set(true, name))(_ => runningGauge.set(false, name)