Skip to content

Commit

Permalink
Issue 20 (#21)
Browse files Browse the repository at this point in the history
* Issue 20

* Issue 20 with tests

* Accept RIO instead of Task for histogram and summary time and time_
  • Loading branch information
toxicafunk authored Jan 9, 2020
1 parent fe5af4e commit 95e2f7d
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 8 deletions.
4 changes: 2 additions & 2 deletions docs/essentials/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ ZIO](https://zio.dev/docs/howto/howto_use_module_pattern) to provide modules for

```scala
// Prometheus
libraryDependencies += "dev.zio" %% "zio-metrics-prometheus" % "0.0.6"
libraryDependencies += "dev.zio" %% "zio-metrics-prometheus" % "0.0.9"

// Dropwizard
libraryDependencies += "dev.zio" %% "zio-metrics-dropwizard" % "0.0.6"
libraryDependencies += "dev.zio" %% "zio-metrics-dropwizard" % "0.0.9"
```

## References
Expand Down
47 changes: 44 additions & 3 deletions docs/essentials/prometheus.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,10 +254,11 @@ http://localhost:9090`. Since we are already using `histogram` in this example,
let's look at it in more detail next.

## Histogram
Histogram has 3 modes we can use:
1. We can ``time`` how long a task/function takes to complete.
Histogram has 4 modes we can use:
1. We can `time` how long a function takes to complete.
2. We can `observe` arbitrary `Double` values.
3. We can start an arbitrary `timer` and `observe` its duration.
4. We can `time` how long a `zio.Task` or a `zio.RIO` takes to complete.

Using a histogram to `time` a function is pretty much what was shown on the
`Exporters` example above, where `() => Thread.sleep(2000)` is the function we
Expand Down Expand Up @@ -324,7 +325,7 @@ We can override the `DefaultBuckets` so:
} yield r
```

Finally, to use an arbitrary timer, after registering the histogram, we need to
To use an arbitrary timer, after registering the histogram, we need to
start the timer using our recently created `histogram` object with the
`startTimer` method which returns a `timer` object. We will use this `timer` to
mark every point we want observed (measure) thus giving us the duration between
Expand Down Expand Up @@ -354,6 +355,29 @@ We could define `ExponentialBuckets` substituting the following line:

`h <- histogram.register("duration_histogram", Array("method"), ExponentialBuckets(0.25,2,5))`


Finally, we have two variations of `time`, one that returns the duration and the
result of executing the `Task` or `RIO` and another (`time_`) that only executes the
`Task` and returns the result, but not the duration. The benefit of `time_` is
that it uses `ZIO.bracket` underneath to have stronger guarantees on the
aquisition, use and release of the timer and the task execution.

```scala mdoc:silent
import zio.Task

val testHistogramTask: RIO[PrometheusRegistry, (CollectorRegistry, Double, String)] = for {
h <- Histogram("task_histogram_timer", Array.empty[String], DefaultBuckets(Seq.empty[Double]))
(d,s) <- h.time(Task{Thread.sleep(2000); "Success"})
r <- registry.getCurrent()
} yield (r, d, s)

val testHistogramTask2: RIO[PrometheusRegistry, (CollectorRegistry, String)] = for {
h <- histogram.register("task_histogram_timer_")
a <- h.time_(Task{Thread.sleep(2000); "Success"})
r <- registry.getCurrent()
} yield (r, a)
```

Now lets inspect our values by `tap`ping our `RIO`. Add ` with Clock.Live`
to the runtime `rt` before executing the following code.

Expand Down Expand Up @@ -390,3 +414,20 @@ documentation for more information.
} yield r
```

Just like `Histogram` it has methods `time` and `time_` that take a `Task` or
`RIO` as input.

```scala mdoc:silent
val testSummaryTask: RIO[PrometheusRegistry, CollectorRegistry] = for {
s <- summary.register("task_summary_timer")
_ <- s.time(Task(Thread.sleep(2000)))
r <- registry.getCurrent()
} yield r

val testSummaryTask2: RIO[PrometheusRegistry, CollectorRegistry] = for {
s <- summary.register("task_summary_timer_")
_ <- s.time_(Task(Thread.sleep(2000)))
r <- registry.getCurrent()
} yield r
```

39 changes: 38 additions & 1 deletion prometheus/src/main/scala/zio/metrics/prometheus/Metrics.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package zio.metrics.prometheus

import zio.{ RIO, Task }
import zio.{ RIO, Task, UIO }
import zio.metrics.prometheus.PrometheusRegistry.{ Percentile, Tolerance }
import io.prometheus.client.{ Counter => PCounter, Gauge => PGauge }
import io.prometheus.client.{ Histogram => PHistogram, Summary => PSummary }
Expand Down Expand Up @@ -118,6 +118,21 @@ class Histogram(private val pHistogram: PHistogram) extends Metric {
f()
t.observeDuration()
}

def time[R, A](task: RIO[R, A]): RIO[R, (Double, A)] =
time(task, Array.empty[String])

def time[R, A](task: RIO[R, A], labelNames: Array[String]): RIO[R, (Double, A)] = {
val t = if (labelNames.isEmpty) pHistogram.startTimer() else pHistogram.labels(labelNames: _*).startTimer()
task >>= (a => Task((t.observeDuration(), a)))
}

def time_[R, A](task: RIO[R, A]): RIO[R, A] =
time_(task, Array.empty[String])

def time_[R, A](task: RIO[R, A], labelNames: Array[String]): RIO[R, A] =
Task(if (labelNames.isEmpty) pHistogram.startTimer() else pHistogram.labels(labelNames: _*).startTimer())
.bracket(t => UIO(t.close))(_ => task)
}

object Histogram {
Expand All @@ -136,18 +151,40 @@ class Summary(private val pSummary: PSummary) extends Metric {
def observe(amount: Double, labelNames: Array[String]): Task[Unit] =
Task(if (labelNames.isEmpty) pSummary.observe(amount) else pSummary.labels(labelNames: _*).observe(amount))

def startTimer(): Task[SummaryTimer] =
startTimer(Array.empty[String])

def startTimer(labelNames: Array[String]): Task[SummaryTimer] =
Task(if (labelNames.isEmpty) pSummary.startTimer() else pSummary.labels(labelNames: _*).startTimer)

def observeDuration(timer: SummaryTimer): Task[Double] =
Task(timer.observeDuration())

def time(f: () => Unit): Task[Double] =
time(f, Array.empty[String])

def time(f: () => Unit, labelNames: Array[String]): Task[Double] =
Task {
val t = if (labelNames.isEmpty) pSummary.startTimer() else pSummary.labels(labelNames: _*).startTimer()
f()
t.observeDuration()
}

def time[R, A](task: RIO[R, A]): RIO[R, (Double, A)] =
time(task, Array.empty[String])

def time[R, A](task: RIO[R, A], labelNames: Array[String]): RIO[R, (Double, A)] = {
val t = if (labelNames.isEmpty) pSummary.startTimer() else pSummary.labels(labelNames: _*).startTimer()
task >>= (a => RIO((t.observeDuration(), a)))
}

def time_[R, A](task: RIO[R, A]): RIO[R, A] =
time_(task, Array.empty[String])

def time_[R, A](task: RIO[R, A], labelNames: Array[String]): RIO[R, A] =
RIO
.effect(if (labelNames.isEmpty) pSummary.startTimer() else pSummary.labels(labelNames: _*).startTimer())
.bracket(t => UIO(t.close))(_ => task)
}

object Summary {
Expand Down
75 changes: 73 additions & 2 deletions prometheus/src/test/scala/zio/metrics/PrometheusTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package zio.metrics

import java.util

import zio.{ RIO, Runtime }
import zio.{ RIO, Runtime, Task }
import zio.console.{ putStrLn, Console }
import testz.{ assert, Harness, PureHarness, Result }
import io.prometheus.client.CollectorRegistry
Expand Down Expand Up @@ -54,12 +54,36 @@ object PrometheusTest {
r <- registry.getCurrent()
} yield r

val testHistogramTask: RIO[PrometheusRegistry, (CollectorRegistry, Double, String)] = for {
h <- Histogram("task_histogram_timer", Array.empty[String], DefaultBuckets(Seq.empty[Double]))
(d, s) <- h.time(Task { Thread.sleep(2000); "Success" })
r <- registry.getCurrent()
} yield (r, d, s)

val testHistogramTask2: RIO[PrometheusRegistry, (CollectorRegistry, String)] = for {
h <- histogram.register("task_histogram_timer_")
a <- h.time_(Task { Thread.sleep(2000); "Success" })
r <- registry.getCurrent()
} yield (r, a)

val testSummary: RIO[PrometheusRegistry, CollectorRegistry] = for {
s <- Summary("simple_summary", Array.empty[String], List.empty[(Double, Double)])
_ <- RIO.foreach(List(10.5, 25.0, 50.7, 57.3, 19.8))(s.observe(_))
r <- registry.getCurrent()
} yield r

val testSummaryTask: RIO[PrometheusRegistry, CollectorRegistry] = for {
s <- summary.register("task_summary_timer")
_ <- s.time(Task(Thread.sleep(2000)))
r <- registry.getCurrent()
} yield r

val testSummaryTask2: RIO[PrometheusRegistry, CollectorRegistry] = for {
s <- summary.register("task_summary_timer_")
_ <- s.time_(Task(Thread.sleep(2000)))
r <- registry.getCurrent()
} yield r

def tests[T](harness: Harness[T]): T = {
import harness._
section(
Expand Down Expand Up @@ -116,12 +140,37 @@ object PrometheusTest {
set.add("simple_histogram_timer_count")
set.add("simple_histogram_timer_sum")

val r = rt.unsafeRun(testHistogramTimer.tap(r => exporters.write004(r).map(println)))
val r = rt.unsafeRun(testHistogramTimer)

val count = r.filteredMetricFamilySamples(set).nextElement().samples.get(0).value
val sum = r.filteredMetricFamilySamples(set).nextElement().samples.get(1).value
Result.combine(assert(count == 1.0), assert(sum >= 2.0 && sum <= 3.0))
},
test("histogram timer accepts tasks") { () =>
val set: util.Set[String] = new util.HashSet[String]()
set.add("task_histogram_timer_count")
set.add("task_histogram_timer_sum")

val r = rt.unsafeRun(testHistogramTask)

val count = r._1.filteredMetricFamilySamples(set).nextElement().samples.get(0).value
val sum = r._1.filteredMetricFamilySamples(set).nextElement().samples.get(1).value

println(s"Timed Task returns ${r._3} after ${r._2}")

Result.combine(assert(count == 1.0 && sum >= 2.0 && sum <= 3.0), assert(r._3 == "Success"))
},
test("histogram timer_ accepts tasks") { () =>
val set: util.Set[String] = new util.HashSet[String]()
set.add("task_histogram_timer__count")
set.add("task_histogram_timer__sum")

val r = rt.unsafeRun(testHistogramTask2)

val count = r._1.filteredMetricFamilySamples(set).nextElement().samples.get(0).value
val sum = r._1.filteredMetricFamilySamples(set).nextElement().samples.get(1).value
Result.combine(assert(count == 1.0 && sum >= 2.0 && sum <= 3.0), assert(r._2 == "Success"))
},
test("summary count and sum are as expected") { () =>
val set: util.Set[String] = new util.HashSet[String]()
set.add("simple_summary_count")
Expand All @@ -131,6 +180,28 @@ object PrometheusTest {
val count = r.filteredMetricFamilySamples(set).nextElement().samples.get(0).value
val sum = r.filteredMetricFamilySamples(set).nextElement().samples.get(1).value
Result.combine(assert(count == 5.0), assert(sum == 163.3))
},
test("summary timer accepts tasks") { () =>
val set: util.Set[String] = new util.HashSet[String]()
set.add("task_summary_timer_count")
set.add("task_summary_timer_sum")

val r = rt.unsafeRun(testSummaryTask)

val count = r.filteredMetricFamilySamples(set).nextElement().samples.get(0).value
val sum = r.filteredMetricFamilySamples(set).nextElement().samples.get(1).value
Result.combine(assert(count == 1.0), assert(sum >= 2.0 && sum <= 3.0))
},
test("summary timer_ accepts tasks") { () =>
val set: util.Set[String] = new util.HashSet[String]()
set.add("task_summary_timer__count")
set.add("task_summary_timer__sum")

val r = rt.unsafeRun(testSummaryTask2.tap(r => exporters.write004(r).map(println)))

val count = r.filteredMetricFamilySamples(set).nextElement().samples.get(0).value
val sum = r.filteredMetricFamilySamples(set).nextElement().samples.get(1).value
Result.combine(assert(count == 1.0), assert(sum >= 2.0 && sum <= 3.0))
}
)
}
Expand Down

0 comments on commit 95e2f7d

Please sign in to comment.