All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.twitter.finagle.stats.StatsFormatter.scala Maven / Gradle / Ivy

There is a newer version: 6.39.0
Show newest version
package com.twitter.finagle.stats

import com.twitter.app.GlobalFlag
import scala.collection.Map
import scala.collection.mutable
import scala.util.matching.Regex

object format extends GlobalFlag[String](
  "commonsmetrics",
  "Format style for metric names (ostrich|commonsmetrics|commonsstats)"
) {
  private[stats] val Ostrich = "ostrich"
  private[stats] val CommonsMetrics = "commonsmetrics"
  private[stats] val CommonsStats = "commonsstats"
}

/**
 * Allows for customization of how stat names get formatted.
 */
private[stats] sealed trait StatsFormatter {

  def apply(values: SampledValues): Map[String, Number] = {
    val results = new mutable.HashMap[String, Number]()
    results ++= values.gauges
    results ++= values.counters

    values.histograms.foreach { case (name, snapshot) =>
      results += histoName(name, "count") -> snapshot.count
      results += histoName(name, "sum") -> snapshot.sum
      results += histoName(name, labelAverage) -> snapshot.avg
      results += histoName(name, labelMin) -> snapshot.min
      results += histoName(name, labelMax) -> snapshot.max

      for (p <- snapshot.percentiles) {
        val percentileName = histoName(name, labelPercentile(p.getQuantile))
        results += percentileName -> p.getValue
      }
    }
    results
  }

  /**
   * Returns the full formatted name of histogram.
   *
   * @param name the "name" of the histogram
   * @param component a single part of this histogram, for example the average,
   *                  count, or a percentile.
   */
  protected def histoName(name: String, component: String): String

  /** Label applied for a given percentile, `p`, of a histogram */
  protected def labelPercentile(p: Double): String

  /** Label applied for the minimum of a histogram */
  protected def labelMin: String

  /** Label applied for the maximum of a histogram */
  protected def labelMax: String

  /** Label applied for the average of a histogram */
  protected def labelAverage: String
}

private[stats] object StatsFormatter {

  /**
   * Uses the global flag, [[format]], to select the formatter used.
   */
  def default: StatsFormatter =
    format() match {
      case format.Ostrich => Ostrich
      case format.CommonsMetrics => CommonsMetrics
      case format.CommonsStats => CommonsStats
    }

  /**
   * The default behavior for formatting as done by Commons Metrics.
   *
   * See Commons Metrics' `Metrics.sample()`.
   */
  object CommonsMetrics extends StatsFormatter {
    protected def histoName(name: String, component: String): String =
      s"$name.$component"

    protected def labelPercentile(p: Double): String = {
      // this has a strange quirk that p999 gets formatted as p9990
      val gname: String = "p" + (p * 10000).toInt
      if (3 < gname.length && ("00" == gname.substring(3))) {
        gname.substring(0, 3)
      } else {
        gname
      }
    }

    protected def labelMin: String = "min"

    protected def labelMax: String = "max"

    protected def labelAverage: String = "avg"
  }

  /**
   * Replicates the behavior for formatting Ostrich stats.
   *
   * See Ostrich's `Distribution.toMap`.
   */
  object Ostrich extends StatsFormatter {
    protected def histoName(name: String, component: String): String =
      s"$name.$component"

    protected def labelPercentile(p: Double): String = {
      p match {
        case 0.5d => "p50"
        case 0.9d => "p90"
        case 0.95d => "p95"
        case 0.99d => "p99"
        case 0.999d => "p999"
        case 0.9999d => "p9999"
        case _ =>
          val padded = (p * 10000).toInt
          s"p$padded"
      }
    }

    protected def labelMin: String = "minimum"

    protected def labelMax: String = "maximum"

    protected def labelAverage: String = "average"
  }

  /**
   * Replicates the behavior for formatting Commons Stats stats.
   *
   * See Commons Stats' `Stats.getVariables()`.
   */
  object CommonsStats extends StatsFormatter {

    private[this] def inMegabytes(l: Number): Number = l.longValue() / 1048576L
    private[this] def inSeconds(l: Number): Number = l.longValue() / 1000L
    private[this] val gcCycles: Regex = "^jvm_mem_(.*)_cycles$".r
    private[this] val gcMsec: Regex = "^jvm_mem_(.*)_msec$".r

    override def apply(values: SampledValues): Map[String, Number] = {
      val original = super.apply(values)

      original.map {
        case ("jvm_num_cpus", n) => "jvm_available_processors" -> n
        case ("jvm_classes_current_loaded", n) => "jvm_class_loaded_count" -> n
        case ("jvm_classes_total_loaded", n) => "jvm_class_total_loaded_count" -> n
        case ("jvm_classes_total_unloaded", n) => "jvm_class_unloaded_count" -> n
        case ("jvm_gc_msec", n) => "jvm_gc_collection_time_ms" -> n
        case ("jvm_gc_cycles", n) => "jvm_gc_collection_count" -> n
        case ("jvm_heap_committed", n) => "jvm_memory_heap_mb_committed" -> inMegabytes(n)
        case ("jvm_heap_max", n) => "jvm_memory_heap_mb_max" -> inMegabytes(n)
        case ("jvm_heap_used", n) => "jvm_memory_heap_mb_used" -> inMegabytes(n)
        case ("jvm_nonheap_committed", n) => "jvm_memory_non_heap_mb_committed" -> inMegabytes(n)
        case ("jvm_nonheap_max", n) => "jvm_memory_non_heap_mb_max" -> inMegabytes(n)
        case ("jvm_nonheap_used", n) => "jvm_memory_non_heap_mb_used" -> inMegabytes(n)
        case ("jvm_thread_count", n) => "jvm_threads_active" -> n
        case ("jvm_thread_daemon_count", n) => "jvm_threads_daemon" -> n
        case ("jvm_thread_peak_count", n) => "jvm_threads_peak" -> n
        case ("jvm_start_time", n) => "jvm_time_ms" -> n
        case ("jvm_uptime", n) => "jvm_uptime_secs" -> inSeconds(n)
        case (gcCycles(gc), n) => s"jvm_gc_${gc}_collection_count" -> n
        case (gcMsec(gc), n) => s"jvm_gc_${gc}_collection_time_ms" -> n
        case kv => kv
      }
    }

    protected def histoName(name: String, component: String): String =
      s"${name}_$component"

    protected def labelPercentile(p: Double): String =
      s"${p * 100}_percentile".replace(".", "_")

    protected def labelMin: String = "min"

    protected def labelMax: String = "max"

    protected def labelAverage: String = "avg"
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy