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