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