Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
59 changes: 59 additions & 0 deletions core-tests/shared/src/test/scala/zio/metrics/MetricsSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package zio.metrics

import zio.metrics.MetricKeyType.Histogram
import zio.test.assertTrue
import zio.{Chunk, ZIO, ZIOBaseSpec, durationInt}

object MetricsSpec extends ZIOBaseSpec {
private val labels = Set(MetricLabel("x", "a"), MetricLabel("y", "b"))

private val counterName = "test_counter"
private val gaugeName = "test_gauge"
private val frequencyName = "test_frequency"
private val histogramName = "test_histogram"
private val summaryName = "test_summary"

private val counter = Metric.counter(counterName, "description1").tagged(labels).fromConst(1L)
private val gauge = Metric.gauge(gaugeName, "description2").tagged(labels)
private val frequency = Metric.frequency(frequencyName).tagged(labels)
private val histogram =
Metric.histogram(histogramName, Histogram.Boundaries.fromChunk(Chunk(1.0, 2.0, 3.0))).tagged(labels)
private val summary =
Metric.summary(summaryName, 1.minute, 10, 0.0, Chunk(0.1, 0.5, 0.9)).tagged(labels)

def spec = suite("CurrentMetrics")(
test("Metrics prettyPrint should correctly process empty Metric set") {
for {
currentMetrics <- ZIO.succeed(Metrics(Set.empty))
str <- currentMetrics.prettyPrint
} yield assertTrue(str == "")
},
test("should be pretty printed correctly") {
for {
_ <- ZIO.succeed(1.0) @@ counter @@ gauge @@ histogram @@ summary
_ <- ZIO.succeed(3.0) @@ counter @@ gauge @@ histogram @@ summary
_ <- ZIO.succeed("strValue1") @@ frequency
_ <- ZIO.succeed("strValue2") @@ frequency
_ <- ZIO.succeed("strValue1") @@ frequency
//set of metrics in the current snapshot may include metrics from other tests
allMetricsSnapshot <- ZIO.metrics
testSnapshot <-
ZIO.succeed(
Metrics(
allMetricsSnapshot.metrics.filter(m =>
Set(counterName, gaugeName, frequencyName, histogramName, summaryName).contains(m.metricKey.name)
)
)
)
str <- testSnapshot.prettyPrint
} yield assertTrue(
str == s"""test_counter(description1) tags[x: a, y: b] Counter[${2.0}]
|test_frequency tags[x: a, y: b] Frequency[(strValue2 -> 1), (strValue1 -> 2)]
|test_gauge(description2) tags[x: a, y: b] Gauge[${3.0}]
|test_histogram tags[x: a, y: b] Histogram[buckets: [(${1.0} -> 1), (${2.0} -> 1), (${3.0} -> 2), (${1.7976931348623157e308} -> 2)], count: [2], min: [${1.0}], max: [${3.0}], sum: [${4.0}]]
|test_summary tags[x: a, y: b] Summary[quantiles: [(0.1 -> None), (0.5 -> Some(${1.0})), (0.9 -> Some(${1.0}))], count: [2], min: [${1.0}], max: [${3.0}], sum: [${4.0}]]""".stripMargin
)
}
)

}
10 changes: 9 additions & 1 deletion core/shared/src/main/scala/zio/ZIO.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
package zio

import zio.internal.{FiberScope, Platform}
import zio.metrics.MetricLabel
import zio.metrics.{MetricLabel, Metrics}
import zio.stacktracer.TracingImplicits.disableAutoTrace

import java.io.IOException
Expand Down Expand Up @@ -4169,6 +4169,14 @@ object ZIO extends ZIOCompanionPlatformSpecific with ZIOCompanionVersionSpecific
)(zero: => B)(f: (B, A) => B)(implicit trace: Trace): ZIO[R, E, B] =
Ref.make(zero).flatMap(acc => foreachParDiscard(in)(_.flatMap(a => acc.update(f(_, a)))) *> acc.get)

/**
* Gets current metrics snapshot.
*/
def metrics(implicit trace: Trace): UIO[Metrics] =
ZIO.succeedUnsafe { implicit u =>
Metrics(internal.metrics.metricRegistry.snapshot())
}

/**
* Returns a effect that will never produce anything. The moral equivalent of
* `while(true) {}`, only without the wasted CPU cycles. Fibers that execute
Expand Down
74 changes: 74 additions & 0 deletions core/shared/src/main/scala/zio/metrics/Metrics.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package zio.metrics

import zio.{IO, Trace, UIO}

import java.io.IOException

final case class Metrics private[zio] (metrics: Set[MetricPair.Untyped]) {

/**
* Gets all current metrics in pretty debug view.
*/
def prettyPrint(implicit trace: Trace): UIO[String] =
zio.System.lineSeparator.map(renderMetrics(metrics, _))

/**
* Dumps all current metrics to the console.
*/
def dump(implicit trace: Trace): IO[IOException, Unit] =
prettyPrint.flatMap(zio.Console.printLine(_))

private def renderMetrics(metrics: Set[MetricPair.Untyped], lineSeparator: String): String =
if (metrics.nonEmpty) {

val maxNameLength =
metrics.map(m => m.metricKey.name.length + m.metricKey.description.map(_.length + 2).getOrElse(0)).max + 2
val maxTagSectionLength = metrics.map(m => tagsToString(m.metricKey.tags).length).max + 2

metrics
.groupBy(_.metricKey.name)
.toList
.sortBy(_._1)
.map { case (_, groupedMetrics) =>
groupedMetrics
.map(metric =>
renderKey(metric.metricKey, maxNameLength) +
renderTags(metric.metricKey, maxTagSectionLength) +
renderValue(metric)
)
.mkString(lineSeparator)
}
.mkString(lineSeparator)

} else ""

private def renderKey(key: MetricKey[_], padTo: Int): String =
s"${key.name}${key.description.map(d => s"($d)").getOrElse("")}".padTo(padTo, ' ')

private def renderTags(key: MetricKey[_], padTo: Int): String = {
val tagsStr = tagsToString(key.tags)
tagsStr + " " * math.max(0, padTo - tagsStr.length)
}

private def renderValue(metric: MetricPair[_, _]): String = {
def renderKeyValues(keyValues: Iterable[(Any, Any)]): String =
keyValues.map(p => s"(${p._1} -> ${p._2})").mkString(", ")

metric.metricState match {
case MetricState.Counter(count) => s"Counter[$count]"
case MetricState.Frequency(occurrences) =>
s"Frequency[${renderKeyValues(occurrences)}]"
case MetricState.Gauge(value) => s"Gauge[$value]"
case MetricState.Histogram(buckets, count, min, max, sum) =>
s"Histogram[buckets: [${renderKeyValues(buckets)}], count: [$count], min: [$min], max: [$max], sum: [$sum]]"
case MetricState.Summary(_, quantiles, count, min, max, sum) =>
s"Summary[quantiles: [${renderKeyValues(quantiles)}], count: [$count], min: [$min], max: [$max], sum: [$sum]]"
}
}

private def tagsToString(tags: Set[MetricLabel]): String = {
val byName = tags.toList.sortBy(_.key)
"tags[" + byName.map(l => s"${l.key}: ${l.value}").mkString(", ") + "]"
}

}