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
|
|
}
|