github.com/intfoundation/intchain@v0.0.0-20220727031208-4316ad31ca73/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/intfoundation/intchain/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 (self *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.Tick(self.Interval) 46 metricsApi := &LibratoClient{self.Email, self.Token} 47 for now := range ticker { 48 var metrics Batch 49 var err error 50 if metrics, err = self.BuildRequest(now, self.Registry); err != nil { 51 log.Printf("ERROR constructing librato request body %s", err) 52 continue 53 } 54 if err := metricsApi.PostMetrics(metrics); err != nil { 55 log.Printf("ERROR sending metrics to librato %s", err) 56 continue 57 } 58 } 59 } 60 61 // calculate sum of squares from data provided by metrics.Histogram 62 // see http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods 63 func sumSquares(s metrics.Sample) float64 { 64 count := float64(s.Count()) 65 sumSquared := math.Pow(count*s.Mean(), 2) 66 sumSquares := math.Pow(count*s.StdDev(), 2) + sumSquared/count 67 if math.IsNaN(sumSquares) { 68 return 0.0 69 } 70 return sumSquares 71 } 72 func sumSquaresTimer(t metrics.Timer) float64 { 73 count := float64(t.Count()) 74 sumSquared := math.Pow(count*t.Mean(), 2) 75 sumSquares := math.Pow(count*t.StdDev(), 2) + sumSquared/count 76 if math.IsNaN(sumSquares) { 77 return 0.0 78 } 79 return sumSquares 80 } 81 82 func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Batch, err error) { 83 snapshot = Batch{ 84 // coerce timestamps to a stepping fn so that they line up in Librato graphs 85 MeasureTime: (now.Unix() / self.intervalSec) * self.intervalSec, 86 Source: self.Source, 87 } 88 snapshot.Gauges = make([]Measurement, 0) 89 snapshot.Counters = make([]Measurement, 0) 90 histogramGaugeCount := 1 + len(self.Percentiles) 91 r.Each(func(name string, metric interface{}) { 92 if self.Namespace != "" { 93 name = fmt.Sprintf("%s.%s", self.Namespace, name) 94 } 95 measurement := Measurement{} 96 measurement[Period] = self.Interval.Seconds() 97 switch m := metric.(type) { 98 case metrics.Counter: 99 if m.Count() > 0 { 100 measurement[Name] = fmt.Sprintf("%s.%s", name, "count") 101 measurement[Value] = float64(m.Count()) 102 measurement[Attributes] = map[string]interface{}{ 103 DisplayUnitsLong: Operations, 104 DisplayUnitsShort: OperationsShort, 105 DisplayMin: "0", 106 } 107 snapshot.Counters = append(snapshot.Counters, measurement) 108 } 109 case metrics.Gauge: 110 measurement[Name] = name 111 measurement[Value] = float64(m.Value()) 112 snapshot.Gauges = append(snapshot.Gauges, measurement) 113 case metrics.GaugeFloat64: 114 measurement[Name] = name 115 measurement[Value] = m.Value() 116 snapshot.Gauges = append(snapshot.Gauges, measurement) 117 case metrics.Histogram: 118 if m.Count() > 0 { 119 gauges := make([]Measurement, histogramGaugeCount) 120 s := m.Sample() 121 measurement[Name] = fmt.Sprintf("%s.%s", name, "hist") 122 measurement[Count] = uint64(s.Count()) 123 measurement[Max] = float64(s.Max()) 124 measurement[Min] = float64(s.Min()) 125 measurement[Sum] = float64(s.Sum()) 126 measurement[SumSquares] = sumSquares(s) 127 gauges[0] = measurement 128 for i, p := range self.Percentiles { 129 gauges[i+1] = Measurement{ 130 Name: fmt.Sprintf("%s.%.2f", measurement[Name], p), 131 Value: s.Percentile(p), 132 Period: measurement[Period], 133 } 134 } 135 snapshot.Gauges = append(snapshot.Gauges, gauges...) 136 } 137 case metrics.Meter: 138 measurement[Name] = name 139 measurement[Value] = float64(m.Count()) 140 snapshot.Counters = append(snapshot.Counters, measurement) 141 snapshot.Gauges = append(snapshot.Gauges, 142 Measurement{ 143 Name: fmt.Sprintf("%s.%s", name, "1min"), 144 Value: m.Rate1(), 145 Period: int64(self.Interval.Seconds()), 146 Attributes: map[string]interface{}{ 147 DisplayUnitsLong: Operations, 148 DisplayUnitsShort: OperationsShort, 149 DisplayMin: "0", 150 }, 151 }, 152 Measurement{ 153 Name: fmt.Sprintf("%s.%s", name, "5min"), 154 Value: m.Rate5(), 155 Period: int64(self.Interval.Seconds()), 156 Attributes: map[string]interface{}{ 157 DisplayUnitsLong: Operations, 158 DisplayUnitsShort: OperationsShort, 159 DisplayMin: "0", 160 }, 161 }, 162 Measurement{ 163 Name: fmt.Sprintf("%s.%s", name, "15min"), 164 Value: m.Rate15(), 165 Period: int64(self.Interval.Seconds()), 166 Attributes: map[string]interface{}{ 167 DisplayUnitsLong: Operations, 168 DisplayUnitsShort: OperationsShort, 169 DisplayMin: "0", 170 }, 171 }, 172 ) 173 case metrics.Timer: 174 measurement[Name] = name 175 measurement[Value] = float64(m.Count()) 176 snapshot.Counters = append(snapshot.Counters, measurement) 177 if m.Count() > 0 { 178 libratoName := fmt.Sprintf("%s.%s", name, "timer.mean") 179 gauges := make([]Measurement, histogramGaugeCount) 180 gauges[0] = Measurement{ 181 Name: libratoName, 182 Count: uint64(m.Count()), 183 Sum: m.Mean() * float64(m.Count()), 184 Max: float64(m.Max()), 185 Min: float64(m.Min()), 186 SumSquares: sumSquaresTimer(m), 187 Period: int64(self.Interval.Seconds()), 188 Attributes: self.TimerAttributes, 189 } 190 for i, p := range self.Percentiles { 191 gauges[i+1] = Measurement{ 192 Name: fmt.Sprintf("%s.timer.%2.0f", name, p*100), 193 Value: m.Percentile(p), 194 Period: int64(self.Interval.Seconds()), 195 Attributes: self.TimerAttributes, 196 } 197 } 198 snapshot.Gauges = append(snapshot.Gauges, gauges...) 199 snapshot.Gauges = append(snapshot.Gauges, 200 Measurement{ 201 Name: fmt.Sprintf("%s.%s", name, "rate.1min"), 202 Value: m.Rate1(), 203 Period: int64(self.Interval.Seconds()), 204 Attributes: map[string]interface{}{ 205 DisplayUnitsLong: Operations, 206 DisplayUnitsShort: OperationsShort, 207 DisplayMin: "0", 208 }, 209 }, 210 Measurement{ 211 Name: fmt.Sprintf("%s.%s", name, "rate.5min"), 212 Value: m.Rate5(), 213 Period: int64(self.Interval.Seconds()), 214 Attributes: map[string]interface{}{ 215 DisplayUnitsLong: Operations, 216 DisplayUnitsShort: OperationsShort, 217 DisplayMin: "0", 218 }, 219 }, 220 Measurement{ 221 Name: fmt.Sprintf("%s.%s", name, "rate.15min"), 222 Value: m.Rate15(), 223 Period: int64(self.Interval.Seconds()), 224 Attributes: map[string]interface{}{ 225 DisplayUnitsLong: Operations, 226 DisplayUnitsShort: OperationsShort, 227 DisplayMin: "0", 228 }, 229 }, 230 ) 231 } 232 } 233 }) 234 return 235 }