app/story_packages/metrics/FrontendMetrics.scala (62 lines of code) (raw):

package story_packages.metrics import java.util.concurrent.atomic.AtomicLong import com.amazonaws.services.cloudwatch.model.StandardUnit import org.joda.time.DateTime import story_packages.util.Box import scala.util.Try sealed trait DataPoint { val value: Long val time: Option[DateTime] } case class DurationDataPoint(value: Long, time: Option[DateTime] = None) extends DataPoint case class CountDataPoint(value: Long) extends DataPoint { val time: Option[DateTime] = None } case class GaugeDataPoint(value: Long) extends DataPoint { val time: Option[DateTime] = None } case class FrontendStatisticSet(metric: FrontendMetric, datapoints: List[DataPoint]) { lazy val sampleCount: Double = datapoints.size lazy val maximum: Double = Try(datapoints.maxBy(_.value).value).getOrElse(0L).toDouble lazy val minimum: Double = Try(datapoints.minBy(_.value).value).getOrElse(0L).toDouble lazy val sum: Double = datapoints.map(_.value).sum.toDouble lazy val average: Double = Try(sum / sampleCount).toOption.getOrElse(0L) def reset(): Unit = metric.putDataPoints(datapoints) } sealed trait FrontendMetric { val name: String val metricUnit: StandardUnit def getAndResetDataPoints: List[DataPoint] def putDataPoints(points: List[DataPoint]): Unit def isEmpty: Boolean } case class GaugeMetric(name: String, description: String, get: () => Long, metricUnit: StandardUnit = StandardUnit.Megabytes) extends FrontendMetric { def getAndResetDataPoints: List[DataPoint] = List(GaugeDataPoint(get())) def putDataPoints(points: List[DataPoint]): Unit = () def isEmpty: Boolean = false } case class CountMetric(name: String, description: String) extends FrontendMetric { private val count: AtomicLong = new AtomicLong(0L) val metricUnit = StandardUnit.Count def getAndResetDataPoints: List[DataPoint] = List(CountDataPoint(count.getAndSet(0L))) def getAndReset: Long = getAndResetDataPoints.map(_.value).reduce(_ + _) def putDataPoints(points: List[DataPoint]): Unit = for(dataPoint <- points) count.addAndGet(dataPoint.value) def getResettingValue(): Long = count.get() def record(): Unit = count.incrementAndGet() def increment(): Unit = record() def isEmpty: Boolean = count.get() == 0L } case class DurationMetric(name: String, metricUnit: StandardUnit) extends FrontendMetric { private val dataPoints: Box[List[DataPoint]] = Box(List[DurationDataPoint]()) def getDataPoints: List[DataPoint] = dataPoints.get() def getAndResetDataPoints: List[DataPoint] = { val points = dataPoints.get() dataPoints.alter(_.diff(points)) points } def putDataPoints(points: List[DataPoint]): Unit = dataPoints.alter(points ::: _) def record(dataPoint: DurationDataPoint): Unit = dataPoints.alter(dataPoint :: _) def recordDuration(timeInMillis: Long): Unit = record(DurationDataPoint(timeInMillis, Option(DateTime.now))) def isEmpty: Boolean = dataPoints.get().isEmpty }