From df680b100a09466f2ffd1c4b5e6a8ce9f378d2ff Mon Sep 17 00:00:00 2001 From: Pavel Kefurt Date: Tue, 15 Mar 2022 11:31:54 +0100 Subject: [PATCH] PerKey metrics with forKeys method (#63) --- .../scalaeffectapi/perkey/PerKeyHelper.scala | 19 ++++++++++++++ .../scalaeffectapi/perkey/PerKeyMetric.scala | 1 + .../perkey/impl/PerKeyGaugeFactoryImpl.scala | 21 ++++++++------- .../perkey/impl/PerKeyMetricImpl.scala | 13 +++++++--- .../perkey/impl/PerKeyOpsImpl.scala | 26 ++++++++++--------- 5 files changed, 56 insertions(+), 24 deletions(-) create mode 100644 scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/PerKeyHelper.scala diff --git a/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/PerKeyHelper.scala b/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/PerKeyHelper.scala new file mode 100644 index 0000000..828a7f1 --- /dev/null +++ b/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/PerKeyHelper.scala @@ -0,0 +1,19 @@ +package com.avast.metrics.scalaeffectapi.perkey + +import cats.data.NonEmptyList +import com.avast.metrics.scalaeffectapi.Monitor + +private[perkey] object PerKeyHelper { + type MetricBuilder[T] = NonEmptyList[String] => T + + def metricBuilder[F[_], T]( + monitor: Monitor[F] + )(baseName: String, instanceBuilder: (Monitor[F], String) => T): MetricBuilder[T] = { keys => + if (keys.length == 1) { + instanceBuilder(monitor.named(baseName), keys.last) + } else { + val tailWithoutLast = keys.tail.dropRight(1) + instanceBuilder(monitor.named(baseName, keys.head, tailWithoutLast: _*), keys.last) + } + } +} diff --git a/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/PerKeyMetric.scala b/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/PerKeyMetric.scala index 43e8185..c7549b3 100644 --- a/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/PerKeyMetric.scala +++ b/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/PerKeyMetric.scala @@ -2,4 +2,5 @@ package com.avast.metrics.scalaeffectapi.perkey trait PerKeyMetric[M] { def forKey(str: String): M + def forKeys(name: String, name2: String, names: String*): M } diff --git a/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/impl/PerKeyGaugeFactoryImpl.scala b/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/impl/PerKeyGaugeFactoryImpl.scala index a675a0c..b3102de 100644 --- a/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/impl/PerKeyGaugeFactoryImpl.scala +++ b/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/impl/PerKeyGaugeFactoryImpl.scala @@ -1,36 +1,39 @@ package com.avast.metrics.scalaeffectapi.perkey.impl import cats.effect.std.Dispatcher +import com.avast.metrics.scalaeffectapi.perkey.PerKeyHelper.MetricBuilder import com.avast.metrics.scalaeffectapi.{Gauge, Monitor, SettableGauge} -import com.avast.metrics.scalaeffectapi.perkey.{PerKeyGaugeFactory, PerKeyMetric} +import com.avast.metrics.scalaeffectapi.perkey.{PerKeyGaugeFactory, PerKeyHelper, PerKeyMetric} import scala.collection.concurrent.TrieMap class PerKeyGaugeFactoryImpl[F[_]](monitor: Monitor[F]) extends PerKeyGaugeFactory[F] { private def emptyMap[M] = TrieMap.empty[String, M] + private def metricBuilder[T]: (String, (Monitor[F], String) => T) => MetricBuilder[T] = PerKeyHelper.metricBuilder(monitor) + override def settableLong(baseName: String, replaceExisting: Boolean = false): PerKeyMetric[SettableGauge[F, Long]] = { - val instanceBuilder = monitor.named(baseName) - new PerKeyMetricImpl[SettableGauge[F, Long]](emptyMap[SettableGauge[F, Long]], instanceBuilder.gauge.settableLong(_, replaceExisting)) + val instanceBuilder: (Monitor[F], String) => SettableGauge[F, Long] = (m, n) => m.gauge.settableLong(n, replaceExisting) + new PerKeyMetricImpl[SettableGauge[F, Long]](emptyMap[SettableGauge[F, Long]], metricBuilder(baseName, instanceBuilder)) } override def settableDouble(baseName: String, replaceExisting: Boolean = false): PerKeyMetric[SettableGauge[F, Double]] = { - val instanceBuilder = monitor.named(baseName) + val instanceBuilder: (Monitor[F], String) => SettableGauge[F, Double] = (m, n) => m.gauge.settableDouble(n, replaceExisting) new PerKeyMetricImpl[SettableGauge[F, Double]]( emptyMap[SettableGauge[F, Double]], - instanceBuilder.gauge.settableDouble(_, replaceExisting) + metricBuilder(baseName, instanceBuilder) ) } override def generic[T](baseName: String, replaceExisting: Boolean = false)(retrieveValue: () => T): PerKeyMetric[Gauge[F, T]] = { - val instanceBuilder = monitor.named(baseName) - new PerKeyMetricImpl[Gauge[F, T]](emptyMap[Gauge[F, T]], instanceBuilder.gauge.generic(_, replaceExisting)(retrieveValue)) + val instanceBuilder: (Monitor[F], String) => Gauge[F, T] = (m, n) => m.gauge.generic(n, replaceExisting)(retrieveValue) + new PerKeyMetricImpl[Gauge[F, T]](emptyMap[Gauge[F, T]], metricBuilder(baseName, instanceBuilder)) } override def genericWithUnsafeRun[T](baseName: String, replaceExisting: Boolean = false)(retrieveValue: F[T])(implicit dispatcher: Dispatcher[F] ): PerKeyMetric[Gauge[F, T]] = { - val instanceBuilder = monitor.named(baseName) - new PerKeyMetricImpl[Gauge[F, T]](emptyMap[Gauge[F, T]], instanceBuilder.gauge.genericWithUnsafeRun(_, replaceExisting)(retrieveValue)) + val instanceBuilder: (Monitor[F], String) => Gauge[F, T] = (m, n) => m.gauge.genericWithUnsafeRun(n, replaceExisting)(retrieveValue) + new PerKeyMetricImpl[Gauge[F, T]](emptyMap[Gauge[F, T]], metricBuilder(baseName, instanceBuilder)) } } diff --git a/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/impl/PerKeyMetricImpl.scala b/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/impl/PerKeyMetricImpl.scala index 0e48b04..c4384bd 100644 --- a/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/impl/PerKeyMetricImpl.scala +++ b/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/impl/PerKeyMetricImpl.scala @@ -1,11 +1,18 @@ package com.avast.metrics.scalaeffectapi.perkey.impl +import cats.data.NonEmptyList +import com.avast.metrics.scalaeffectapi.perkey.PerKeyHelper.MetricBuilder import com.avast.metrics.scalaeffectapi.perkey.PerKeyMetric import scala.collection.concurrent.{Map => CMap} -private[perkey] class PerKeyMetricImpl[A](map: CMap[String, A], metricBuilder: String => A) extends PerKeyMetric[A] { - override def forKey(str: String): A = { - map.getOrElseUpdate(str, metricBuilder(str)) +private[perkey] class PerKeyMetricImpl[A](map: CMap[String, A], metricBuilder: MetricBuilder[A]) extends PerKeyMetric[A] { + override def forKey(name: String): A = { + map.getOrElseUpdate(name, metricBuilder(NonEmptyList(name, Nil))) + } + + override def forKeys(name: String, name2: String, names: String*): A = { + val keys = List(name, name2) ::: names.toList + map.getOrElseUpdate(keys.mkString("."), metricBuilder(NonEmptyList(name, keys.tail))) } } diff --git a/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/impl/PerKeyOpsImpl.scala b/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/impl/PerKeyOpsImpl.scala index b6abc09..8ab1e42 100644 --- a/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/impl/PerKeyOpsImpl.scala +++ b/scala-effect-api/src/main/scala/com/avast/metrics/scalaeffectapi/perkey/impl/PerKeyOpsImpl.scala @@ -1,40 +1,42 @@ package com.avast.metrics.scalaeffectapi.perkey.impl import com.avast.metrics.scalaeffectapi._ -import com.avast.metrics.scalaeffectapi.perkey.{PerKeyGaugeFactory, PerKeyMetric, PerKeyOps} +import com.avast.metrics.scalaeffectapi.perkey.PerKeyHelper.MetricBuilder +import com.avast.metrics.scalaeffectapi.perkey.{PerKeyGaugeFactory, PerKeyHelper, PerKeyMetric, PerKeyOps} import scala.collection.concurrent.TrieMap private[perkey] class PerKeyOpsImpl[F[_]](monitor: Monitor[F]) extends PerKeyOps[F] { private def emptyMap[M] = TrieMap.empty[String, M] + private def metricBuilder[T]: (String, (Monitor[F], String) => T) => MetricBuilder[T] = PerKeyHelper.metricBuilder(monitor) + override def meter(baseName: String): PerKeyMetric[Meter[F]] = { - val instanceBuilder = monitor.named(baseName) - new PerKeyMetricImpl[Meter[F]](emptyMap[Meter[F]], instanceBuilder.meter) + val instanceBuilder: (Monitor[F], String) => Meter[F] = (m, n) => m.meter(n) + new PerKeyMetricImpl[Meter[F]](emptyMap[Meter[F]], metricBuilder(baseName, instanceBuilder)) } override def counter(baseName: String): PerKeyMetric[Counter[F]] = { - val instanceBuilder = monitor.named(baseName) - new PerKeyMetricImpl[Counter[F]](emptyMap, instanceBuilder.counter) + val instanceBuilder: (Monitor[F], String) => Counter[F] = (m, n) => m.counter(n) + new PerKeyMetricImpl[Counter[F]](emptyMap, metricBuilder(baseName, instanceBuilder)) } override def timer(baseName: String): PerKeyMetric[Timer[F]] = { - val instanceBuilder = monitor.named(baseName) - new PerKeyMetricImpl[Timer[F]](emptyMap, instanceBuilder.timer) + val instanceBuilder: (Monitor[F], String) => Timer[F] = (m, n) => m.timer(n) + new PerKeyMetricImpl[Timer[F]](emptyMap, metricBuilder(baseName, instanceBuilder)) } override def timerPair(baseName: String): PerKeyMetric[TimerPair[F]] = { - val instanceBuilder = monitor.named(baseName) - new PerKeyMetricImpl[TimerPair[F]](emptyMap, instanceBuilder.timerPair) + val instanceBuilder: (Monitor[F], String) => TimerPair[F] = (m, n) => m.timerPair(n) + new PerKeyMetricImpl[TimerPair[F]](emptyMap, metricBuilder(baseName, instanceBuilder)) } override def histogram(baseName: String): PerKeyMetric[Histogram[F]] = { - val instanceBuilder = monitor.named(baseName) - new PerKeyMetricImpl[Histogram[F]](emptyMap, instanceBuilder.histogram) + val instanceBuilder: (Monitor[F], String) => Histogram[F] = (m, n) => m.histogram(n) + new PerKeyMetricImpl[Histogram[F]](emptyMap, metricBuilder(baseName, instanceBuilder)) } override def gauge: PerKeyGaugeFactory[F] = { new PerKeyGaugeFactoryImpl[F](monitor) } - }