Telemetry

object Implementation extends StrictLogging {
  def foo = BusinessLogic.formatSQL("SELECT * FROM foo;")
}
object TelemetryV1 extends StrictLogging {
  def metered[A](f: () => A): A = {
    val start  = System.currentTimeMillis()
    val result = f()
    val end    = System.currentTimeMillis()
    logger.info(s"elapsed: ${end - start}")
    result
  }
}
object Implementation extends StrictLogging {
  def foo = TelemetryV1.metered(
  	() => BusinessLogic.formatSQL("SELECT * FROM foo;")
  )
}
//elapsed: 10s
//elapsed: 1s
//elapsed: 20s
object TelemetryV2 extends StrictLogging {
  def metered[A](name: String)(f: () => A): A = {
    val start  = System.currentTimeMillis()
    val result = f()
    val end    = System.currentTimeMillis()
    logger.info(s"name: ${name} elapsed: ${end - start}")
    result
  }
}
object ImplementationV2 {
  def foo =
    TelemetryV2.metered("foo")(
      () => BusinessLogic.formatSQL("SELECT * FROM foo;")
    )

  def bar =
    TelemetryV2.metered("bar")(
      () => BusinessLogic.formatSQL("SELECT * FROM foo;")
    )
}
//name: "foo" elapsed: 10s
//name: "bar" elapsed: 1s
//name: "foo" elapsed: 20s
object TelemetryV3 extends StrictLogging {
  def metered[A](name: String)(f: () => A): A = {
    val start  = System.currentTimeMillis()
    val result = Try(f())
    val end    = System.currentTimeMillis()
    logger.info(s"name: ${name} error: ${result.isFailure} elapsed: ${end - start}")
    result.get
  }
}
object ImplementationV3 {
  def foo =
    TelemetryV3.metered("foo")(
      () => BusinessLogic.formatSQL("SELECT * FROM foo;")
    )

  def bar =
    TelemetryV3.metered("bar")(
      () => BusinessLogic.formatSQL("SELECT * FROM foo;")
    )
}
//name: "foo" error: true elapsed: 10s
//name: "bar" error: false elapsed: 1s
//name: "foo" error: true elapsed: 20s
object BusinessLogicV1 {
  def formatSQL(mode: String)(sql: String): String = ???
}
object TelemetryV4 extends StrictLogging {
  def metered[A](
                  name: String,
                  tags: Map[String, Any]
                )(f: () => A): A = {
    val start  = System.currentTimeMillis()
    val result = Try(f())
    val end    = System.currentTimeMillis()
    val tags   = (Map("error" -> result.isFailure) ++ tags).map{
      case (k,v) => s"$k: $v"
    }.toList.mkString(" ")
    logger.info(s"name: ${name} ${tags} elapsed: ${end - start}")
    result.get
  }
}
object ImplementationV4 {
  def foo(mode: String) =
    TelemetryV4.metered(
      "foo",
      Map("mode" -> mode)
    )(() => BusinessLogicV1.formatSQL(mode)("SELECT * FROM foo;"))

  def bar(mode: String) =
    TelemetryV4.metered(
      "bar",
      Map("mode" -> mode)
    )(() => BusinessLogicV1.formatSQL(mode)("SELECT * FROM foo;"))
}
//name: "foo" error: true  mode: "http"  elapsed: 10s
//name: "bar" error: false mode: "local" elapsed: 1s
//name: "foo" error: true  mode: "http"  elapsed: 20s
//name: "foo" error: true  mode: "http"  elapsed: 10s
//name: "bar" error: false mode: "local" elapsed: 1s
//name: "foo" error: true  mode: "http"  elapsed: 20s
//name: "foo" error: true  mode: "http"  elapsed: 20s
//name: "foo" error: true  mode: "http"  elapsed: 20s
//name: "foo" error: true  mode: "http"  elapsed: 20s
//name: "foo" error: true  mode: "http"  elapsed: 20s
//name: "foo" error: true  mode: "http"  elapsed: 30s
//name: "foo" error: true  mode: "http"  elapsed: 20s
//name: "foo" error: true  mode: "http"  elapsed: 20s

8*50*5*60*60 ~= 0.9MB/h

object TelemetryReporter {
  type Name = String
  type Tags = Map[String, Any]
  private case class Metric(count: Int, sum: Long, max:Long)

  private var buffer = Map.empty[(Name, Tags), Metric]
  
  def addOperation(name: String, tags: Map[String, Any], v: Long) = {
    val newV = buffer
      .get(name -> tags)
      .map(m => m.copy(count = m.count + 1, sum = m.sum + v, max = Math.max(v,m.max)))
      .getOrElse(Metric(1, 0, Long.MinValue))
    buffer = buffer + ((name -> tags) -> newV)
  }

  def reportingTask() = {
    while(true) {
      //report to some external system
      buffer = Map.empty
    }
  }
}

And that's it for telemetry!

Concepts

- name

- tags/labels

- reporter

- aggregation

Made with Slides.com