237 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			237 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
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
 | 
						|
}
 |