github.com/theQRL/go-zond@v0.1.1/metrics/librato/librato.go (about) 1 package librato 2 3 import ( 4 "fmt" 5 "log" 6 "math" 7 "regexp" 8 "time" 9 10 "github.com/theQRL/go-zond/metrics" 11 ) 12 13 // a regexp for extracting the unit from time.Duration.String 14 var unitRegexp = regexp.MustCompile(`[^\\d]+$`) 15 16 // a helper that turns a time.Duration into librato display attributes for timer metrics 17 func translateTimerAttributes(d time.Duration) (attrs map[string]interface{}) { 18 attrs = make(map[string]interface{}) 19 attrs[DisplayTransform] = fmt.Sprintf("x/%d", int64(d)) 20 attrs[DisplayUnitsShort] = string(unitRegexp.Find([]byte(d.String()))) 21 return 22 } 23 24 type Reporter struct { 25 Email, Token string 26 Namespace string 27 Source string 28 Interval time.Duration 29 Registry metrics.Registry 30 Percentiles []float64 // percentiles to report on histogram metrics 31 TimerAttributes map[string]interface{} // units in which timers will be displayed 32 intervalSec int64 33 } 34 35 func NewReporter(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) *Reporter { 36 return &Reporter{e, t, "", s, d, r, p, translateTimerAttributes(u), int64(d / time.Second)} 37 } 38 39 func Librato(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) { 40 NewReporter(r, d, e, t, s, p, u).Run() 41 } 42 43 func (rep *Reporter) Run() { 44 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") 45 ticker := time.NewTicker(rep.Interval) 46 defer ticker.Stop() 47 metricsApi := &LibratoClient{rep.Email, rep.Token} 48 for now := range ticker.C { 49 var metrics Batch 50 var err error 51 if metrics, err = rep.BuildRequest(now, rep.Registry); err != nil { 52 log.Printf("ERROR constructing librato request body %s", err) 53 continue 54 } 55 if err := metricsApi.PostMetrics(metrics); err != nil { 56 log.Printf("ERROR sending metrics to librato %s", err) 57 continue 58 } 59 } 60 } 61 62 // calculate sum of squares from data provided by metrics.Histogram 63 // see http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods 64 func sumSquares(icount int64, mean, stDev float64) float64 { 65 count := float64(icount) 66 sumSquared := math.Pow(count*mean, 2) 67 sumSquares := math.Pow(count*stDev, 2) + sumSquared/count 68 if math.IsNaN(sumSquares) { 69 return 0.0 70 } 71 return sumSquares 72 } 73 func sumSquaresTimer(t metrics.TimerSnapshot) float64 { 74 count := float64(t.Count()) 75 sumSquared := math.Pow(count*t.Mean(), 2) 76 sumSquares := math.Pow(count*t.StdDev(), 2) + sumSquared/count 77 if math.IsNaN(sumSquares) { 78 return 0.0 79 } 80 return sumSquares 81 } 82 83 func (rep *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Batch, err error) { 84 snapshot = Batch{ 85 // coerce timestamps to a stepping fn so that they line up in Librato graphs 86 MeasureTime: (now.Unix() / rep.intervalSec) * rep.intervalSec, 87 Source: rep.Source, 88 } 89 snapshot.Gauges = make([]Measurement, 0) 90 snapshot.Counters = make([]Measurement, 0) 91 histogramGaugeCount := 1 + len(rep.Percentiles) 92 r.Each(func(name string, metric interface{}) { 93 if rep.Namespace != "" { 94 name = fmt.Sprintf("%s.%s", rep.Namespace, name) 95 } 96 measurement := Measurement{} 97 measurement[Period] = rep.Interval.Seconds() 98 switch m := metric.(type) { 99 case metrics.Counter: 100 ms := m.Snapshot() 101 if ms.Count() > 0 { 102 measurement[Name] = fmt.Sprintf("%s.%s", name, "count") 103 measurement[Value] = float64(ms.Count()) 104 measurement[Attributes] = map[string]interface{}{ 105 DisplayUnitsLong: Operations, 106 DisplayUnitsShort: OperationsShort, 107 DisplayMin: "0", 108 } 109 snapshot.Counters = append(snapshot.Counters, measurement) 110 } 111 case metrics.CounterFloat64: 112 if count := m.Snapshot().Count(); count > 0 { 113 measurement[Name] = fmt.Sprintf("%s.%s", name, "count") 114 measurement[Value] = count 115 measurement[Attributes] = map[string]interface{}{ 116 DisplayUnitsLong: Operations, 117 DisplayUnitsShort: OperationsShort, 118 DisplayMin: "0", 119 } 120 snapshot.Counters = append(snapshot.Counters, measurement) 121 } 122 case metrics.Gauge: 123 measurement[Name] = name 124 measurement[Value] = float64(m.Snapshot().Value()) 125 snapshot.Gauges = append(snapshot.Gauges, measurement) 126 case metrics.GaugeFloat64: 127 measurement[Name] = name 128 measurement[Value] = m.Snapshot().Value() 129 snapshot.Gauges = append(snapshot.Gauges, measurement) 130 case metrics.GaugeInfo: 131 measurement[Name] = name 132 measurement[Value] = m.Snapshot().Value() 133 snapshot.Gauges = append(snapshot.Gauges, measurement) 134 case metrics.Histogram: 135 ms := m.Snapshot() 136 if ms.Count() > 0 { 137 gauges := make([]Measurement, histogramGaugeCount) 138 measurement[Name] = fmt.Sprintf("%s.%s", name, "hist") 139 measurement[Count] = uint64(ms.Count()) 140 measurement[Max] = float64(ms.Max()) 141 measurement[Min] = float64(ms.Min()) 142 measurement[Sum] = float64(ms.Sum()) 143 measurement[SumSquares] = sumSquares(ms.Count(), ms.Mean(), ms.StdDev()) 144 gauges[0] = measurement 145 for i, p := range rep.Percentiles { 146 gauges[i+1] = Measurement{ 147 Name: fmt.Sprintf("%s.%.2f", measurement[Name], p), 148 Value: ms.Percentile(p), 149 Period: measurement[Period], 150 } 151 } 152 snapshot.Gauges = append(snapshot.Gauges, gauges...) 153 } 154 case metrics.Meter: 155 ms := m.Snapshot() 156 measurement[Name] = name 157 measurement[Value] = float64(ms.Count()) 158 snapshot.Counters = append(snapshot.Counters, measurement) 159 snapshot.Gauges = append(snapshot.Gauges, 160 Measurement{ 161 Name: fmt.Sprintf("%s.%s", name, "1min"), 162 Value: ms.Rate1(), 163 Period: int64(rep.Interval.Seconds()), 164 Attributes: map[string]interface{}{ 165 DisplayUnitsLong: Operations, 166 DisplayUnitsShort: OperationsShort, 167 DisplayMin: "0", 168 }, 169 }, 170 Measurement{ 171 Name: fmt.Sprintf("%s.%s", name, "5min"), 172 Value: ms.Rate5(), 173 Period: int64(rep.Interval.Seconds()), 174 Attributes: map[string]interface{}{ 175 DisplayUnitsLong: Operations, 176 DisplayUnitsShort: OperationsShort, 177 DisplayMin: "0", 178 }, 179 }, 180 Measurement{ 181 Name: fmt.Sprintf("%s.%s", name, "15min"), 182 Value: ms.Rate15(), 183 Period: int64(rep.Interval.Seconds()), 184 Attributes: map[string]interface{}{ 185 DisplayUnitsLong: Operations, 186 DisplayUnitsShort: OperationsShort, 187 DisplayMin: "0", 188 }, 189 }, 190 ) 191 case metrics.Timer: 192 ms := m.Snapshot() 193 measurement[Name] = name 194 measurement[Value] = float64(ms.Count()) 195 snapshot.Counters = append(snapshot.Counters, measurement) 196 if ms.Count() > 0 { 197 libratoName := fmt.Sprintf("%s.%s", name, "timer.mean") 198 gauges := make([]Measurement, histogramGaugeCount) 199 gauges[0] = Measurement{ 200 Name: libratoName, 201 Count: uint64(ms.Count()), 202 Sum: ms.Mean() * float64(ms.Count()), 203 Max: float64(ms.Max()), 204 Min: float64(ms.Min()), 205 SumSquares: sumSquaresTimer(ms), 206 Period: int64(rep.Interval.Seconds()), 207 Attributes: rep.TimerAttributes, 208 } 209 for i, p := range rep.Percentiles { 210 gauges[i+1] = Measurement{ 211 Name: fmt.Sprintf("%s.timer.%2.0f", name, p*100), 212 Value: ms.Percentile(p), 213 Period: int64(rep.Interval.Seconds()), 214 Attributes: rep.TimerAttributes, 215 } 216 } 217 snapshot.Gauges = append(snapshot.Gauges, gauges...) 218 snapshot.Gauges = append(snapshot.Gauges, 219 Measurement{ 220 Name: fmt.Sprintf("%s.%s", name, "rate.1min"), 221 Value: ms.Rate1(), 222 Period: int64(rep.Interval.Seconds()), 223 Attributes: map[string]interface{}{ 224 DisplayUnitsLong: Operations, 225 DisplayUnitsShort: OperationsShort, 226 DisplayMin: "0", 227 }, 228 }, 229 Measurement{ 230 Name: fmt.Sprintf("%s.%s", name, "rate.5min"), 231 Value: ms.Rate5(), 232 Period: int64(rep.Interval.Seconds()), 233 Attributes: map[string]interface{}{ 234 DisplayUnitsLong: Operations, 235 DisplayUnitsShort: OperationsShort, 236 DisplayMin: "0", 237 }, 238 }, 239 Measurement{ 240 Name: fmt.Sprintf("%s.%s", name, "rate.15min"), 241 Value: ms.Rate15(), 242 Period: int64(rep.Interval.Seconds()), 243 Attributes: map[string]interface{}{ 244 DisplayUnitsLong: Operations, 245 DisplayUnitsShort: OperationsShort, 246 DisplayMin: "0", 247 }, 248 }, 249 ) 250 } 251 } 252 }) 253 return 254 }