| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- package librato
- import (
- "fmt"
- "log"
- "math"
- "regexp"
- "time"
- "github.com/ethereum/go-ethereum/metrics"
- )
- // a regexp for extracting the unit from time.Duration.String
- var unitRegexp = regexp.MustCompile(`[^\\d]+$`)
- // a helper that turns a time.Duration into librato display attributes for timer metrics
- func translateTimerAttributes(d time.Duration) (attrs map[string]interface{}) {
- attrs = make(map[string]interface{})
- attrs[DisplayTransform] = fmt.Sprintf("x/%d", int64(d))
- attrs[DisplayUnitsShort] = string(unitRegexp.Find([]byte(d.String())))
- return
- }
- type Reporter struct {
- Email, Token string
- Namespace string
- Source string
- Interval time.Duration
- Registry metrics.Registry
- Percentiles []float64 // percentiles to report on histogram metrics
- TimerAttributes map[string]interface{} // units in which timers will be displayed
- intervalSec int64
- }
- func NewReporter(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) *Reporter {
- return &Reporter{e, t, "", s, d, r, p, translateTimerAttributes(u), int64(d / time.Second)}
- }
- func Librato(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) {
- NewReporter(r, d, e, t, s, p, u).Run()
- }
- func (rep *Reporter) Run() {
- log.Printf("WARNING: This client has been DEPRECATED! It has been moved to https://github.com/mihasya/go-metrics-librato and will be removed from rcrowley/go-metrics on August 5th 2015")
- ticker := time.NewTicker(rep.Interval)
- defer ticker.Stop()
- metricsApi := &LibratoClient{rep.Email, rep.Token}
- for now := range ticker.C {
- var metrics Batch
- var err error
- if metrics, err = rep.BuildRequest(now, rep.Registry); err != nil {
- log.Printf("ERROR constructing librato request body %s", err)
- continue
- }
- if err := metricsApi.PostMetrics(metrics); err != nil {
- log.Printf("ERROR sending metrics to librato %s", err)
- continue
- }
- }
- }
- // calculate sum of squares from data provided by metrics.Histogram
- // see http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
- func sumSquares(s metrics.Sample) float64 {
- count := float64(s.Count())
- sumSquared := math.Pow(count*s.Mean(), 2)
- sumSquares := math.Pow(count*s.StdDev(), 2) + sumSquared/count
- if math.IsNaN(sumSquares) {
- return 0.0
- }
- return sumSquares
- }
- func sumSquaresTimer(t metrics.Timer) float64 {
- count := float64(t.Count())
- sumSquared := math.Pow(count*t.Mean(), 2)
- sumSquares := math.Pow(count*t.StdDev(), 2) + sumSquared/count
- if math.IsNaN(sumSquares) {
- return 0.0
- }
- return sumSquares
- }
- func (rep *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Batch, err error) {
- snapshot = Batch{
- // coerce timestamps to a stepping fn so that they line up in Librato graphs
- MeasureTime: (now.Unix() / rep.intervalSec) * rep.intervalSec,
- Source: rep.Source,
- }
- snapshot.Gauges = make([]Measurement, 0)
- snapshot.Counters = make([]Measurement, 0)
- histogramGaugeCount := 1 + len(rep.Percentiles)
- r.Each(func(name string, metric interface{}) {
- if rep.Namespace != "" {
- name = fmt.Sprintf("%s.%s", rep.Namespace, name)
- }
- measurement := Measurement{}
- measurement[Period] = rep.Interval.Seconds()
- switch m := metric.(type) {
- case metrics.Counter:
- if m.Count() > 0 {
- measurement[Name] = fmt.Sprintf("%s.%s", name, "count")
- measurement[Value] = float64(m.Count())
- measurement[Attributes] = map[string]interface{}{
- DisplayUnitsLong: Operations,
- DisplayUnitsShort: OperationsShort,
- DisplayMin: "0",
- }
- snapshot.Counters = append(snapshot.Counters, measurement)
- }
- case metrics.Gauge:
- measurement[Name] = name
- measurement[Value] = float64(m.Value())
- snapshot.Gauges = append(snapshot.Gauges, measurement)
- case metrics.GaugeFloat64:
- measurement[Name] = name
- measurement[Value] = m.Value()
- snapshot.Gauges = append(snapshot.Gauges, measurement)
- case metrics.Histogram:
- if m.Count() > 0 {
- gauges := make([]Measurement, histogramGaugeCount)
- s := m.Sample()
- measurement[Name] = fmt.Sprintf("%s.%s", name, "hist")
- measurement[Count] = uint64(s.Count())
- measurement[Max] = float64(s.Max())
- measurement[Min] = float64(s.Min())
- measurement[Sum] = float64(s.Sum())
- measurement[SumSquares] = sumSquares(s)
- gauges[0] = measurement
- for i, p := range rep.Percentiles {
- gauges[i+1] = Measurement{
- Name: fmt.Sprintf("%s.%.2f", measurement[Name], p),
- Value: s.Percentile(p),
- Period: measurement[Period],
- }
- }
- snapshot.Gauges = append(snapshot.Gauges, gauges...)
- }
- case metrics.Meter:
- measurement[Name] = name
- measurement[Value] = float64(m.Count())
- snapshot.Counters = append(snapshot.Counters, measurement)
- snapshot.Gauges = append(snapshot.Gauges,
- Measurement{
- Name: fmt.Sprintf("%s.%s", name, "1min"),
- Value: m.Rate1(),
- Period: int64(rep.Interval.Seconds()),
- Attributes: map[string]interface{}{
- DisplayUnitsLong: Operations,
- DisplayUnitsShort: OperationsShort,
- DisplayMin: "0",
- },
- },
- Measurement{
- Name: fmt.Sprintf("%s.%s", name, "5min"),
- Value: m.Rate5(),
- Period: int64(rep.Interval.Seconds()),
- Attributes: map[string]interface{}{
- DisplayUnitsLong: Operations,
- DisplayUnitsShort: OperationsShort,
- DisplayMin: "0",
- },
- },
- Measurement{
- Name: fmt.Sprintf("%s.%s", name, "15min"),
- Value: m.Rate15(),
- Period: int64(rep.Interval.Seconds()),
- Attributes: map[string]interface{}{
- DisplayUnitsLong: Operations,
- DisplayUnitsShort: OperationsShort,
- DisplayMin: "0",
- },
- },
- )
- case metrics.Timer:
- measurement[Name] = name
- measurement[Value] = float64(m.Count())
- snapshot.Counters = append(snapshot.Counters, measurement)
- if m.Count() > 0 {
- libratoName := fmt.Sprintf("%s.%s", name, "timer.mean")
- gauges := make([]Measurement, histogramGaugeCount)
- gauges[0] = Measurement{
- Name: libratoName,
- Count: uint64(m.Count()),
- Sum: m.Mean() * float64(m.Count()),
- Max: float64(m.Max()),
- Min: float64(m.Min()),
- SumSquares: sumSquaresTimer(m),
- Period: int64(rep.Interval.Seconds()),
- Attributes: rep.TimerAttributes,
- }
- for i, p := range rep.Percentiles {
- gauges[i+1] = Measurement{
- Name: fmt.Sprintf("%s.timer.%2.0f", name, p*100),
- Value: m.Percentile(p),
- Period: int64(rep.Interval.Seconds()),
- Attributes: rep.TimerAttributes,
- }
- }
- snapshot.Gauges = append(snapshot.Gauges, gauges...)
- snapshot.Gauges = append(snapshot.Gauges,
- Measurement{
- Name: fmt.Sprintf("%s.%s", name, "rate.1min"),
- Value: m.Rate1(),
- Period: int64(rep.Interval.Seconds()),
- Attributes: map[string]interface{}{
- DisplayUnitsLong: Operations,
- DisplayUnitsShort: OperationsShort,
- DisplayMin: "0",
- },
- },
- Measurement{
- Name: fmt.Sprintf("%s.%s", name, "rate.5min"),
- Value: m.Rate5(),
- Period: int64(rep.Interval.Seconds()),
- Attributes: map[string]interface{}{
- DisplayUnitsLong: Operations,
- DisplayUnitsShort: OperationsShort,
- DisplayMin: "0",
- },
- },
- Measurement{
- Name: fmt.Sprintf("%s.%s", name, "rate.15min"),
- Value: m.Rate15(),
- Period: int64(rep.Interval.Seconds()),
- Attributes: map[string]interface{}{
- DisplayUnitsLong: Operations,
- DisplayUnitsShort: OperationsShort,
- DisplayMin: "0",
- },
- },
- )
- }
- }
- })
- return
- }
|