Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package com.twitter.finagle.stats
import com.twitter.logging.Logger
import com.twitter.util.lint.{Category, Issue, Rule}
import java.util
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.LongAdder
import scala.collection.JavaConverters._
import scala.util.control.NonFatal
object Metrics {
private val log = Logger.get()
private def defaultHistogramFactory(
name: String,
percentiles: IndexedSeq[Double]
): MetricsHistogram = new MetricsBucketedHistogram(name, percentiles)
// represents a real instance of a gauge or a counter
private sealed trait Repr
private case object GaugeRepr extends Repr
private case object CounterRepr extends Repr
private val DefaultMetricsMaps: MetricsMaps = newMetricsMaps
/**
* Create a new [[Metrics]] that does not share gauges, counters, or stats (by default,
* these are shared across [[Metrics]] instances)
*/
def createDetached(
mkHistogram: (String, IndexedSeq[Double]) => MetricsHistogram,
separator: String
): Metrics =
new Metrics(mkHistogram, separator, newMetricsMaps)
def createDetached(): Metrics =
createDetached(Metrics.defaultHistogramFactory, scopeSeparator())
private[this] def newMetricsMaps: MetricsMaps = MetricsMaps(
countersMap = new ConcurrentHashMap[Seq[String], MetricsStore.StoreCounter](),
statsMap = new ConcurrentHashMap[Seq[String], MetricsStore.StoreStat](),
gaugesMap = new ConcurrentHashMap[Seq[String], MetricsStore.StoreGauge]()
)
private class StoreCounterImpl(override val name: String) extends MetricsStore.StoreCounter {
private[this] val adder = new LongAdder()
val counter: Counter = new Counter {
def incr(delta: Long): Unit = {
adder.add(delta)
}
}
def count: Long = adder.sum()
}
private class StoreGaugeImpl(override val name: String, f: => Number)
extends MetricsStore.StoreGauge {
override def read: Number = f
}
private class StoreStatImpl(histo: MetricsHistogram, override val name: String, doLog: Boolean)
extends MetricsStore.StoreStat {
def snapshot: Snapshot = histo.snapshot
override val stat: Stat = new Stat {
def add(value: Float): Unit = {
if (doLog)
log.info(s"Stat $name observed $value")
val asLong = value.toLong
histo.add(asLong)
}
}
def clear(): Unit = histo.clear()
}
private case class MetricsMaps(
countersMap: ConcurrentHashMap[Seq[String], MetricsStore.StoreCounter],
statsMap: ConcurrentHashMap[Seq[String], MetricsStore.StoreStat],
gaugesMap: ConcurrentHashMap[Seq[String], MetricsStore.StoreGauge])
}
/**
* Thrown when you try to create a metric which would collide with a pre-existing metric.
*/
private[stats] class MetricCollisionException(msg: String) extends IllegalArgumentException(msg)
/**
* A concrete metrics registry for creating and reading metrics.
*
* This stats implementation respects [[Verbosity verbosity levels]] such that
*
* - it takes [[Verbosity]] as an argument while creating a certain metric and
* - reports verbosity levels via [[MetricsView#verbosity]]
*
* @note For efficiency reasons, it doesn't keep track of default (i.e., [[Verbosity.Default]])
* metrics.
*
* @note A verbosity level is only attached once, when metric is being created. Any subsequent
* creation/querying of the same metric (i.e., metric with the same name), doesn't affect
* its initial verbosity.
*
* @note By default, instances of [[Metrics]] share underlying [[Metrics.MetricsMaps]]. In the
* case of multiple [[StatsReceiver]]s, this avoids duplicate metrics. To use per-instance
* [[Metrics.MetricsMaps]], create the instance using `Metrics.createDetached`.
*/
private[finagle] class Metrics private (
mkHistogram: (String, IndexedSeq[Double]) => MetricsHistogram,
separator: String,
metricsMaps: Metrics.MetricsMaps)
extends MetricsStore
with MetricsView {
def this() = this(Metrics.defaultHistogramFactory, scopeSeparator(), Metrics.DefaultMetricsMaps)
def this(mkHistogram: (String, IndexedSeq[Double]) => MetricsHistogram, separator: String) =
this(mkHistogram, separator, Metrics.DefaultMetricsMaps)
import Metrics._
private[this] val loggedStats: Set[String] = debugLoggedStatNames()
private[this] val countersMap = metricsMaps.countersMap
private[this] val statsMap = metricsMaps.statsMap
private[this] val gaugesMap = metricsMaps.gaugesMap
/**
* Store MetricSchemas for each metric in order to surface metric metadata to users.
*/
private[this] val metricSchemas = new ConcurrentHashMap[String, MetricSchema]()
private[this] val verbosityMap =
new ConcurrentHashMap[String, Verbosity]()
private[this] val reservedNames = new ConcurrentHashMap[String, Repr]()
val histoDetails = new ConcurrentHashMap[String, HistogramDetail]
private[this] def format(names: Seq[String]): String =
names.mkString(separator)
def getOrCreateCounter(schema: CounterSchema): MetricsStore.StoreCounter = {
val counter = countersMap.get(schema.metricBuilder.name)
if (counter != null)
return counter
val formatted = format(schema.metricBuilder.name)
val curNameUsage = reservedNames.putIfAbsent(formatted, CounterRepr)
if (curNameUsage == null || curNameUsage == CounterRepr) {
val next = new Metrics.StoreCounterImpl(formatted)
val prev = countersMap.putIfAbsent(schema.metricBuilder.name, next)
if (schema.metricBuilder.verbosity != Verbosity.Default)
verbosityMap.put(formatted, schema.metricBuilder.verbosity)
if (prev != null) {
prev
} else {
metricSchemas.put(formatted, schema)
next
}
} else {
throw new MetricCollisionException(
s"A gauge with the name $formatted had already" +
" been defined when you tried to add a new counter."
)
}
}
def getOrCreateStat(schema: HistogramSchema): MetricsStore.StoreStat = {
val stat = statsMap.get(schema.metricBuilder.name)
if (stat != null)
return stat
if (schema.metricBuilder.percentiles.isEmpty) {
createStat(
HistogramSchema(schema.metricBuilder.withPercentiles(BucketedHistogram.DefaultQuantiles)))
} else {
createStat(schema)
}
}
private def createStat(schema: HistogramSchema): MetricsStore.StoreStat = {
val formatted = format(schema.metricBuilder.name)
val doLog = loggedStats.contains(formatted)
val histogram = mkHistogram(formatted, schema.metricBuilder.percentiles)
histogram match {
case histo: MetricsBucketedHistogram =>
histoDetails.put(formatted, histo.histogramDetail)
case _ =>
log.debug(s"$formatted's histogram implementation doesn't support details")
}
val next = new Metrics.StoreStatImpl(histogram, formatted, doLog)
val prev = statsMap.putIfAbsent(schema.metricBuilder.name, next)
if (schema.metricBuilder.verbosity != Verbosity.Default) {
verbosityMap.put(formatted, schema.metricBuilder.verbosity)
}
if (prev != null) {
prev
} else {
metricSchemas.put(formatted, schema)
next
}
}
def registerGauge(schema: GaugeSchema, f: => Float): Unit =
registerNumberGauge(schema, f)
def registerLongGauge(schema: GaugeSchema, f: => Long): Unit =
registerNumberGauge(schema, f)
private def registerNumberGauge(schema: GaugeSchema, f: => Number): Unit = {
val formatted = format(schema.metricBuilder.name)
val curNameUsage = reservedNames.putIfAbsent(formatted, GaugeRepr)
if (curNameUsage == null) {
val next = new Metrics.StoreGaugeImpl(formatted, f)
gaugesMap.putIfAbsent(schema.metricBuilder.name, next)
metricSchemas.put(formatted, schema)
if (schema.metricBuilder.verbosity != Verbosity.Default) {
verbosityMap.put(formatted, schema.metricBuilder.verbosity)
}
} else if (curNameUsage == GaugeRepr) {
// it should be impossible to collide with a gauge in finagle since
// StatsReceiverWithCumulativeGauges already protects us.
// we replace existing gauges to support commons metrics behavior.
val next = new Metrics.StoreGaugeImpl(formatted, f)
gaugesMap.put(schema.metricBuilder.name, next)
} else {
throw new MetricCollisionException(
s"A Counter with the name $formatted had already" +
" been defined when you tried to add a new gauge."
)
}
}
def unregisterGauge(names: Seq[String]): Unit = {
gaugesMap.remove(names)
val formatted = format(names)
metricSchemas.remove(formatted)
reservedNames.remove(formatted)
verbosityMap.remove(formatted)
}
def gauges: util.Map[String, Number] = {
val map = new util.HashMap[String, Number](gaugesMap.size)
gaugesMap.elements().asScala.foreach { sg =>
try {
map.put(sg.name, sg.read)
} catch {
case NonFatal(e) => log.warning(e, s"exception while sampling gauge '${sg.name}'")
}
}
util.Collections.unmodifiableMap(map)
}
def counters: util.Map[String, Number] = {
val cs = countersMap
.elements()
.asScala
.map { sc => sc.name -> Long.box(sc.count) }
.toMap
util.Collections.unmodifiableMap(cs.asJava)
}
def histograms: util.Map[String, Snapshot] = {
val snaps = statsMap
.elements()
.asScala
.map { ss => ss.name -> ss.snapshot }
.toMap
util.Collections.unmodifiableMap(snaps.asJava)
}
def verbosity: util.Map[String, Verbosity] =
util.Collections.unmodifiableMap(verbosityMap)
def schemas: util.Map[String, MetricSchema] =
util.Collections.unmodifiableMap(metricSchemas)
def metricsCollisionsLinterRule: Rule =
Rule(
Category.Configuration,
"Metrics name collision",
"Identifies metrics with ambiguous names that collide with other metrics. " +
"""Metrics recorded in a scope Seq("foo", "bar") can collide with Seq("foo/bar") when """ +
s"exporting the metrics to JSON. To fix, never use the separator character $separator " +
"in metrics names.\nThis linter does not account for denylisted metrics, verbosity, " +
"or collisions between Stats and Counters/Gauges."
) {
def toIssue(kind: String, collisions: Iterable[Seq[String]]) =
Issue(
collisions
.map(_.mkString("Seq(\"", "\", \"", "\")"))
.mkString(s"$kind:\n", " collides with\n", "")
)
def toMapWithIssues[V <: MetricsStore.StoreMetric](
map: ConcurrentHashMap[Seq[String], V],
namesToIssue: Iterable[Seq[String]] => Issue
) =
map.asScala
.groupBy { case (_, metric) => metric.name }
.values
.filter(_.size > 1)
.map(collisions => namesToIssue(collisions.keys))
toMapWithIssues(gaugesMap, toIssue("Gauge", _)).toSeq ++
toMapWithIssues(countersMap, toIssue("Counter", _)) ++
toMapWithIssues(statsMap, toIssue("Stat", _))
}
}