Counter

Counter metric, to track counts, running totals, or events.

If your use case can go up or down consider using a Gauge instead. Use the rate() function in Prometheus to calculate the rate of increase of a Counter. By convention, the names of Counters are suffixed by _total. This suffix is added to metric's name by underlying prometheus-metrics library, do not add it yourself.

A Counter is a cumulative metric that represents a single monotonically increasing counter whose value can only increase or be reset to zero on restart. For example, you can use a counter to represent the number of requests served, tasks completed, or errors.

Do not use a counter to expose a value that can decrease. For example, do not use a counter for the number of currently running processes; instead use a gauge.

Imports

import io.chrisdavenport.epimetheus._
import cats.effect._

import cats.effect.unsafe.implicits.global

An Example Counter without Labels:

val noLabelsExample = {
  for {
    pr <- PrometheusRegistry.build[IO]
    successCounter <- Counter.noLabels(
      pr,
      Name("example_success"),
      "Example Counter of Success"
    )
    failureCounter <- Counter.noLabels(
      pr,
      Name("example_failure"),
      "Example Counter of Failure"
    )
    _ <- IO(println("Action Here")).guaranteeCase{
      case Outcome.Succeeded(_) => successCounter.inc
      case _ => failureCounter.inc
    }
    out <- pr.write004
  } yield out
}
// noLabelsExample: IO[String] = FlatMap(
//   ioe = Delay(
//     thunk = io.chrisdavenport.epimetheus.PrometheusRegistry$$$Lambda$11197/0x00007fa08dea8210@144f6d35,
//     event = cats.effect.tracing.TracingEvent$StackTrace
//   ),
//   f = <function1>,
//   event = cats.effect.tracing.TracingEvent$StackTrace
// )

noLabelsExample.unsafeRunSync()
// Action Here
// res0: String = """# HELP example_failure_total Example Counter of Failure
// # TYPE example_failure_total counter
// example_failure_total 0.0
// # HELP example_success_total Example Counter of Success
// # TYPE example_success_total counter
// example_success_total 1.0
// """

An Example of a Counter with Labels:

val labelledExample = {
  for {
    pr <- PrometheusRegistry.build[IO]
    counter <- Counter.labelled(
      pr,
      Name("example"),
      "Example Counter",
      Sized(Label("foo")),
      {s: String => Sized(s)}
    )
    _ <- counter.label("bar").inc
    _ <- counter.label("baz").inc
    out <- pr.write004
  } yield out
}
// labelledExample: IO[String] = FlatMap(
//   ioe = Delay(
//     thunk = io.chrisdavenport.epimetheus.PrometheusRegistry$$$Lambda$11197/0x00007fa08dea8210@73a88a1b,
//     event = cats.effect.tracing.TracingEvent$StackTrace
//   ),
//   f = <function1>,
//   event = cats.effect.tracing.TracingEvent$StackTrace
// )

labelledExample.unsafeRunSync()
// res1: String = """# HELP example_total Example Counter
// # TYPE example_total counter
// example_total{foo="bar"} 1.0
// example_total{foo="baz"} 1.0
// """

An Example of a Counter backed algebra.

sealed trait Foo; case object Bar extends Foo; case object Baz extends Foo;; case object Bar extends Foo; case object Baz extends Foo;; case object Baz extends Foo;;

def fooLabel(f: Foo) = {
  f match {
    case Bar => Sized("bar")
    case Baz => Sized("baz")
  }
}

trait FooAlg[F[_]]{
  def bar: F[Unit]
  def baz: F[Unit]
}; object FooAlg {; object FooAlg {
  def impl[F[_]](c: Counter.UnlabelledCounter[F, Foo]) = new FooAlg[F]{
    def bar: F[Unit] = c.label(Bar).inc
    def baz: F[Unit] = c.label(Baz).inc
  }
}

val fooAgebraExample = {
  for {
    pr <- PrometheusRegistry.build[IO]
    counter <- Counter.labelled(
      pr,
      Name("example"),
      "Example Counter",
      Sized(Label("foo")),
      fooLabel
    )
    foo = FooAlg.impl(counter)
    _ <- foo.bar
    _ <- foo.bar
    _ <- foo.baz
    out <- pr.write004
  } yield out
}
// fooAgebraExample: IO[String] = FlatMap(
//   ioe = Delay(
//     thunk = io.chrisdavenport.epimetheus.PrometheusRegistry$$$Lambda$11197/0x00007fa08dea8210@4268b833,
//     event = cats.effect.tracing.TracingEvent$StackTrace
//   ),
//   f = <function1>,
//   event = cats.effect.tracing.TracingEvent$StackTrace
// )

fooAgebraExample.unsafeRunSync()
// res2: String = """# HELP example_total Example Counter
// # TYPE example_total counter
// example_total{foo="bar"} 2.0
// example_total{foo="baz"} 1.0
// """

We force labels to always match the same size. This will fail to compile.

def incorrectlySized[F[_]: Sync](pr: PrometheusRegistry[F]) = {
  Counter.labelled(pr, Name("fail"), "Example Failure", Sized(Label("color"), Name("method")), {s: String => Sized(s)})
}