Skip to content

Instantly share code, notes, and snippets.

@rschreijer
Last active September 17, 2019 23:53
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rschreijer/75a35fd36a9b63d33b63 to your computer and use it in GitHub Desktop.
Save rschreijer/75a35fd36a9b63d33b63 to your computer and use it in GitHub Desktop.
spray-json protocol for the Metric lib. Serializes a MetricRegistry and/or single Metric objects from Spray. See https://dropwizard.github.io/metrics, https://github.com/spray/spray-json
package spray.json.examples
import java.util.concurrent.TimeUnit
import scala.collection.JavaConverters._
import scala.util.{Try, Success, Failure}
import com.codahale.metrics._
import spray.json._
object MetricRegistryJsonProtocol extends DefaultJsonProtocol with MetricRegistryFormat
trait MetricRegistryFormat extends AnyFormat {
private val rateUnit: TimeUnit = TimeUnit.SECONDS
private val rateFactor: Long = rateUnit.toSeconds(1);
private val rateUnitStr: String = s"events/${rateUnit.name.toLowerCase.dropRight(1)}"
private val durationUnit: TimeUnit = TimeUnit.NANOSECONDS
private val durationFactor: Double = 1d / durationUnit.toNanos(1);
private val durationUnitStr: String = durationUnit.name.toLowerCase
private val showSamples: Boolean = true
implicit object GaugeJsonFormat extends RootJsonFormat[Gauge[_]] {
def write(gauge: Gauge[_]) = Try(gauge.getValue) match {
case Success(value) => JsObject(Map("value" -> AnyJsonFormat.write(value)))
case Failure(err) => JsObject(Map("error" -> JsString(err.toString)))
}
def read(value: JsValue): Gauge[_] = throw new DeserializationException("Cannot deserialize Gauge")
}
implicit object CounterJsonFormat extends RootJsonFormat[Counter] {
def write(counter: Counter) = JsObject(Map("value" -> JsNumber(counter.getCount)))
def read(value: JsValue): Counter = throw new DeserializationException("Cannot deserialize Counter")
}
implicit object HistogramJsonFormat extends RootJsonFormat[Histogram] {
def write(histogram: Histogram) = {
val snapshot: Snapshot = histogram.getSnapshot
JsObject(
Map(
"count" -> JsNumber(histogram.getCount),
"max" -> JsNumber(snapshot.getMax),
"mean" -> JsNumber(snapshot.getMean),
"min" -> JsNumber(snapshot.getMin),
"p50" -> JsNumber(snapshot.getMedian),
"p75" -> JsNumber(snapshot.get75thPercentile),
"p95" -> JsNumber(snapshot.get95thPercentile),
"p98" -> JsNumber(snapshot.get98thPercentile),
"p99" -> JsNumber(snapshot.get99thPercentile),
"p999" -> JsNumber(snapshot.get999thPercentile),
"stddev" -> JsNumber(snapshot.getStdDev),
"values" -> {
if(!showSamples) JsNull
else JsArray(snapshot.getValues.toVector.map(JsNumber(_)))
}
)
)
}
def read(value: JsValue): Histogram = throw new DeserializationException("Cannot deserialize Histogram")
}
implicit object MeterJsonFormat extends RootJsonFormat[Meter] {
def write(meter: Meter) = JsObject(
Map(
"count" -> JsNumber(meter.getCount),
"m15_rate" -> JsNumber(meter.getFifteenMinuteRate * rateFactor),
"m1_rate" -> JsNumber(meter.getOneMinuteRate * rateFactor),
"m5_rate" -> JsNumber(meter.getFiveMinuteRate * rateFactor),
"mean_rate" -> JsNumber(meter.getMeanRate * rateFactor),
"units" -> JsString(rateUnitStr)
)
)
def read(value: JsValue): Meter = throw new DeserializationException("Cannot deserialize Meter")
}
implicit object TimerJsonFormat extends RootJsonFormat[Timer] {
def write(timer: Timer) = {
val snapshot: Snapshot = timer.getSnapshot
JsObject(
Map(
"count" -> JsNumber(timer.getCount),
"max" -> JsNumber(snapshot.getMax * durationFactor),
"mean" -> JsNumber(snapshot.getMean * durationFactor),
"min" -> JsNumber(snapshot.getMin * durationFactor),
"p50" -> JsNumber(snapshot.getMedian * durationFactor),
"p75" -> JsNumber(snapshot.get75thPercentile * durationFactor),
"p95" -> JsNumber(snapshot.get95thPercentile * durationFactor),
"p98" -> JsNumber(snapshot.get98thPercentile * durationFactor),
"p99" -> JsNumber(snapshot.get99thPercentile * durationFactor),
"p999" -> JsNumber(snapshot.get999thPercentile * durationFactor),
"duration_units" -> JsString(durationUnitStr),
"stddev" -> JsNumber(snapshot.getStdDev),
"m15_rate" -> JsNumber(timer.getFifteenMinuteRate * rateFactor),
"m1_rate" -> JsNumber(timer.getOneMinuteRate * rateFactor),
"m5_rate" -> JsNumber(timer.getFiveMinuteRate * rateFactor),
"mean_rate" -> JsNumber(timer.getMeanRate * rateFactor),
"rate_units" -> JsString(rateUnitStr),
"values" -> {
if(!showSamples) JsNull
else JsArray(snapshot.getValues.toVector.map(v => JsNumber(v * durationFactor)))
}
)
)
}
def read(value: JsValue): Timer = throw new DeserializationException("Cannot deserialize Timer")
}
implicit object MetricRegistryJsonFormat extends RootJsonFormat[MetricRegistry] {
def write(metricRegistry: MetricRegistry) = JsObject(
Map[String, JsValue](
"gauges" -> JsObject(metricRegistry.getGauges.asScala.toMap.map {
case (name, gauge) => name -> GaugeJsonFormat.write(gauge)
}),
"counters" -> JsObject(metricRegistry.getCounters.asScala.toMap.map {
case (name, counter) => name -> CounterJsonFormat.write(counter)
}),
"histograms" -> JsObject(metricRegistry.getHistograms.asScala.toMap.map {
case (name, histogram) => name -> HistogramJsonFormat.write(histogram)
}),
"meters" -> JsObject(metricRegistry.getMeters.asScala.toMap.map {
case (name, meter) => name -> MeterJsonFormat.write(meter)
}),
"timers" -> JsObject(metricRegistry.getTimers.asScala.toMap.map {
case (name, timer) => name -> TimerJsonFormat.write(timer)
})
)
)
def read(value: JsValue): MetricRegistry = throw new DeserializationException("Cannot deserialize MetricRegistry")
}
}
trait AnyFormat {
implicit object AnyJsonFormat extends JsonFormat[Any] {
def write(in: Any) = {
if(in.isInstanceOf[java.util.Collection[_]]) writeCollection(in.asInstanceOf[java.util.Collection[_]])
else if(in.isInstanceOf[java.util.Map[_, _]]) writeMap(in.asInstanceOf[java.util.Map[_, _]])
else in match {
case n: Int => JsNumber(n)
case n: Long => JsNumber(n)
case f: Float => JsNumber(f)
case f: Double => JsNumber(f)
case s: String => JsString(s)
case b: Boolean => if(b) JsTrue else JsFalse
}
}
private def writeCollection(in: java.util.Collection[_]): JsValue = {
JsArray(in.asScala.toVector.map(write(_)))
}
private def writeMap(in: java.util.Map[_, _]): JsValue = {
JsObject(in.asScala.toMap.map { case (k, v) => k.toString -> write(v)})
}
def read(value: JsValue) = throw new RuntimeException("JSON read as Any? Weird!")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment