github.com/calmw/ethereum@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/calmw/ethereum/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(s metrics.Sample) float64 { 65 count := float64(s.Count()) 66 sumSquared := math.Pow(count*s.Mean(), 2) 67 sumSquares := math.Pow(count*s.StdDev(), 2) + sumSquared/count 68 if math.IsNaN(sumSquares) { 69 return 0.0 70 } 71 return sumSquares 72 } 73 func sumSquaresTimer(t metrics.Timer) 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 if m.Count() > 0 { 101 measurement[Name] = fmt.Sprintf("%s.%s", name, "count") 102 measurement[Value] = float64(m.Count()) 103 measurement[Attributes] = map[string]interface{}{ 104 DisplayUnitsLong: Operations, 105 DisplayUnitsShort: OperationsShort, 106 DisplayMin: "0", 107 } 108 snapshot.Counters = append(snapshot.Counters, measurement) 109 } 110 case metrics.CounterFloat64: 111 if m.Count() > 0 { 112 measurement[Name] = fmt.Sprintf("%s.%s", name, "count") 113 measurement[Value] = m.Count() 114 measurement[Attributes] = map[string]interface{}{ 115 DisplayUnitsLong: Operations, 116 DisplayUnitsShort: OperationsShort, 117 DisplayMin: "0", 118 } 119 snapshot.Counters = append(snapshot.Counters, measurement) 120 } 121 case metrics.Gauge: 122 measurement[Name] = name 123 measurement[Value] = float64(m.Value()) 124 snapshot.Gauges = append(snapshot.Gauges, measurement) 125 case metrics.GaugeFloat64: 126 measurement[Name] = name 127 measurement[Value] = m.Value() 128 snapshot.Gauges = append(snapshot.Gauges, measurement) 129 case metrics.Histogram: 130 if m.Count() > 0 { 131 gauges := make([]Measurement, histogramGaugeCount) 132 s := m.Sample() 133 measurement[Name] = fmt.Sprintf("%s.%s", name, "hist") 134 measurement[Count] = uint64(s.Count()) 135 measurement[Max] = float64(s.Max()) 136 measurement[Min] = float64(s.Min()) 137 measurement[Sum] = float64(s.Sum()) 138 measurement[SumSquares] = sumSquares(s) 139 gauges[0] = measurement 140 for i, p := range rep.Percentiles { 141 gauges[i+1] = Measurement{ 142 Name: fmt.Sprintf("%s.%.2f", measurement[Name], p), 143 Value: s.Percentile(p), 144 Period: measurement[Period], 145 } 146 } 147 snapshot.Gauges = append(snapshot.Gauges, gauges...) 148 } 149 case metrics.Meter: 150 measurement[Name] = name 151 measurement[Value] = float64(m.Count()) 152 snapshot.Counters = append(snapshot.Counters, measurement) 153 snapshot.Gauges = append(snapshot.Gauges, 154 Measurement{ 155 Name: fmt.Sprintf("%s.%s", name, "1min"), 156 Value: m.Rate1(), 157 Period: int64(rep.Interval.Seconds()), 158 Attributes: map[string]interface{}{ 159 DisplayUnitsLong: Operations, 160 DisplayUnitsShort: OperationsShort, 161 DisplayMin: "0", 162 }, 163 }, 164 Measurement{ 165 Name: fmt.Sprintf("%s.%s", name, "5min"), 166 Value: m.Rate5(), 167 Period: int64(rep.Interval.Seconds()), 168 Attributes: map[string]interface{}{ 169 DisplayUnitsLong: Operations, 170 DisplayUnitsShort: OperationsShort, 171 DisplayMin: "0", 172 }, 173 }, 174 Measurement{ 175 Name: fmt.Sprintf("%s.%s", name, "15min"), 176 Value: m.Rate15(), 177 Period: int64(rep.Interval.Seconds()), 178 Attributes: map[string]interface{}{ 179 DisplayUnitsLong: Operations, 180 DisplayUnitsShort: OperationsShort, 181 DisplayMin: "0", 182 }, 183 }, 184 ) 185 case metrics.Timer: 186 measurement[Name] = name 187 measurement[Value] = float64(m.Count()) 188 snapshot.Counters = append(snapshot.Counters, measurement) 189 if m.Count() > 0 { 190 libratoName := fmt.Sprintf("%s.%s", name, "timer.mean") 191 gauges := make([]Measurement, histogramGaugeCount) 192 gauges[0] = Measurement{ 193 Name: libratoName, 194 Count: uint64(m.Count()), 195 Sum: m.Mean() * float64(m.Count()), 196 Max: float64(m.Max()), 197 Min: float64(m.Min()), 198 SumSquares: sumSquaresTimer(m), 199 Period: int64(rep.Interval.Seconds()), 200 Attributes: rep.TimerAttributes, 201 } 202 for i, p := range rep.Percentiles { 203 gauges[i+1] = Measurement{ 204 Name: fmt.Sprintf("%s.timer.%2.0f", name, p*100), 205 Value: m.Percentile(p), 206 Period: int64(rep.Interval.Seconds()), 207 Attributes: rep.TimerAttributes, 208 } 209 } 210 snapshot.Gauges = append(snapshot.Gauges, gauges...) 211 snapshot.Gauges = append(snapshot.Gauges, 212 Measurement{ 213 Name: fmt.Sprintf("%s.%s", name, "rate.1min"), 214 Value: m.Rate1(), 215 Period: int64(rep.Interval.Seconds()), 216 Attributes: map[string]interface{}{ 217 DisplayUnitsLong: Operations, 218 DisplayUnitsShort: OperationsShort, 219 DisplayMin: "0", 220 }, 221 }, 222 Measurement{ 223 Name: fmt.Sprintf("%s.%s", name, "rate.5min"), 224 Value: m.Rate5(), 225 Period: int64(rep.Interval.Seconds()), 226 Attributes: map[string]interface{}{ 227 DisplayUnitsLong: Operations, 228 DisplayUnitsShort: OperationsShort, 229 DisplayMin: "0", 230 }, 231 }, 232 Measurement{ 233 Name: fmt.Sprintf("%s.%s", name, "rate.15min"), 234 Value: m.Rate15(), 235 Period: int64(rep.Interval.Seconds()), 236 Attributes: map[string]interface{}{ 237 DisplayUnitsLong: Operations, 238 DisplayUnitsShort: OperationsShort, 239 DisplayMin: "0", 240 }, 241 }, 242 ) 243 } 244 } 245 }) 246 return 247 }