github.com/tacshi/go-ethereum@v0.0.0-20230616113857-84a434e20921/metrics/influxdb/influxdb.go (about) 1 package influxdb 2 3 import ( 4 "fmt" 5 uurl "net/url" 6 "time" 7 8 "github.com/influxdata/influxdb/client" 9 "github.com/tacshi/go-ethereum/log" 10 "github.com/tacshi/go-ethereum/metrics" 11 ) 12 13 type reporter struct { 14 reg metrics.Registry 15 interval time.Duration 16 17 url uurl.URL 18 database string 19 username string 20 password string 21 namespace string 22 tags map[string]string 23 24 client *client.Client 25 26 cache map[string]int64 27 } 28 29 // InfluxDB starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval. 30 func InfluxDB(r metrics.Registry, d time.Duration, url, database, username, password, namespace string) { 31 InfluxDBWithTags(r, d, url, database, username, password, namespace, nil) 32 } 33 34 // InfluxDBWithTags starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval with the specified tags 35 func InfluxDBWithTags(r metrics.Registry, d time.Duration, url, database, username, password, namespace string, tags map[string]string) { 36 u, err := uurl.Parse(url) 37 if err != nil { 38 log.Warn("Unable to parse InfluxDB", "url", url, "err", err) 39 return 40 } 41 42 rep := &reporter{ 43 reg: r, 44 interval: d, 45 url: *u, 46 database: database, 47 username: username, 48 password: password, 49 namespace: namespace, 50 tags: tags, 51 cache: make(map[string]int64), 52 } 53 if err := rep.makeClient(); err != nil { 54 log.Warn("Unable to make InfluxDB client", "err", err) 55 return 56 } 57 58 rep.run() 59 } 60 61 // InfluxDBWithTagsOnce runs once an InfluxDB reporter and post the given metrics.Registry with the specified tags 62 func InfluxDBWithTagsOnce(r metrics.Registry, url, database, username, password, namespace string, tags map[string]string) error { 63 u, err := uurl.Parse(url) 64 if err != nil { 65 return fmt.Errorf("unable to parse InfluxDB. url: %s, err: %v", url, err) 66 } 67 68 rep := &reporter{ 69 reg: r, 70 url: *u, 71 database: database, 72 username: username, 73 password: password, 74 namespace: namespace, 75 tags: tags, 76 cache: make(map[string]int64), 77 } 78 if err := rep.makeClient(); err != nil { 79 return fmt.Errorf("unable to make InfluxDB client. err: %v", err) 80 } 81 82 if err := rep.send(); err != nil { 83 return fmt.Errorf("unable to send to InfluxDB. err: %v", err) 84 } 85 86 return nil 87 } 88 89 func (r *reporter) makeClient() (err error) { 90 r.client, err = client.NewClient(client.Config{ 91 URL: r.url, 92 Username: r.username, 93 Password: r.password, 94 Timeout: 10 * time.Second, 95 }) 96 97 return 98 } 99 100 func (r *reporter) run() { 101 intervalTicker := time.NewTicker(r.interval) 102 pingTicker := time.NewTicker(time.Second * 5) 103 104 defer intervalTicker.Stop() 105 defer pingTicker.Stop() 106 107 for { 108 select { 109 case <-intervalTicker.C: 110 if err := r.send(); err != nil { 111 log.Warn("Unable to send to InfluxDB", "err", err) 112 } 113 case <-pingTicker.C: 114 _, _, err := r.client.Ping() 115 if err != nil { 116 log.Warn("Got error while sending a ping to InfluxDB, trying to recreate client", "err", err) 117 118 if err = r.makeClient(); err != nil { 119 log.Warn("Unable to make InfluxDB client", "err", err) 120 } 121 } 122 } 123 } 124 } 125 126 func (r *reporter) send() error { 127 var pts []client.Point 128 129 r.reg.Each(func(name string, i interface{}) { 130 now := time.Now() 131 namespace := r.namespace 132 133 switch metric := i.(type) { 134 case metrics.Counter: 135 count := metric.Count() 136 pts = append(pts, client.Point{ 137 Measurement: fmt.Sprintf("%s%s.count", namespace, name), 138 Tags: r.tags, 139 Fields: map[string]interface{}{ 140 "value": count, 141 }, 142 Time: now, 143 }) 144 case metrics.Gauge: 145 ms := metric.Snapshot() 146 pts = append(pts, client.Point{ 147 Measurement: fmt.Sprintf("%s%s.gauge", namespace, name), 148 Tags: r.tags, 149 Fields: map[string]interface{}{ 150 "value": ms.Value(), 151 }, 152 Time: now, 153 }) 154 case metrics.GaugeFloat64: 155 ms := metric.Snapshot() 156 pts = append(pts, client.Point{ 157 Measurement: fmt.Sprintf("%s%s.gauge", namespace, name), 158 Tags: r.tags, 159 Fields: map[string]interface{}{ 160 "value": ms.Value(), 161 }, 162 Time: now, 163 }) 164 case metrics.Histogram: 165 ms := metric.Snapshot() 166 if ms.Count() > 0 { 167 ps := ms.Percentiles([]float64{0.25, 0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) 168 fields := map[string]interface{}{ 169 "count": ms.Count(), 170 "max": ms.Max(), 171 "mean": ms.Mean(), 172 "min": ms.Min(), 173 "stddev": ms.StdDev(), 174 "variance": ms.Variance(), 175 "p25": ps[0], 176 "p50": ps[1], 177 "p75": ps[2], 178 "p95": ps[3], 179 "p99": ps[4], 180 "p999": ps[5], 181 "p9999": ps[6], 182 } 183 pts = append(pts, client.Point{ 184 Measurement: fmt.Sprintf("%s%s.histogram", namespace, name), 185 Tags: r.tags, 186 Fields: fields, 187 Time: now, 188 }) 189 } 190 case metrics.Meter: 191 ms := metric.Snapshot() 192 pts = append(pts, client.Point{ 193 Measurement: fmt.Sprintf("%s%s.meter", namespace, name), 194 Tags: r.tags, 195 Fields: map[string]interface{}{ 196 "count": ms.Count(), 197 "m1": ms.Rate1(), 198 "m5": ms.Rate5(), 199 "m15": ms.Rate15(), 200 "mean": ms.RateMean(), 201 }, 202 Time: now, 203 }) 204 case metrics.Timer: 205 ms := metric.Snapshot() 206 ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) 207 pts = append(pts, client.Point{ 208 Measurement: fmt.Sprintf("%s%s.timer", namespace, name), 209 Tags: r.tags, 210 Fields: map[string]interface{}{ 211 "count": ms.Count(), 212 "max": ms.Max(), 213 "mean": ms.Mean(), 214 "min": ms.Min(), 215 "stddev": ms.StdDev(), 216 "variance": ms.Variance(), 217 "p50": ps[0], 218 "p75": ps[1], 219 "p95": ps[2], 220 "p99": ps[3], 221 "p999": ps[4], 222 "p9999": ps[5], 223 "m1": ms.Rate1(), 224 "m5": ms.Rate5(), 225 "m15": ms.Rate15(), 226 "meanrate": ms.RateMean(), 227 }, 228 Time: now, 229 }) 230 case metrics.ResettingTimer: 231 t := metric.Snapshot() 232 233 if len(t.Values()) > 0 { 234 ps := t.Percentiles([]float64{50, 95, 99}) 235 val := t.Values() 236 pts = append(pts, client.Point{ 237 Measurement: fmt.Sprintf("%s%s.span", namespace, name), 238 Tags: r.tags, 239 Fields: map[string]interface{}{ 240 "count": len(val), 241 "max": val[len(val)-1], 242 "mean": t.Mean(), 243 "min": val[0], 244 "p50": ps[0], 245 "p95": ps[1], 246 "p99": ps[2], 247 }, 248 Time: now, 249 }) 250 } 251 } 252 }) 253 254 bps := client.BatchPoints{ 255 Points: pts, 256 Database: r.database, 257 } 258 259 _, err := r.client.Write(bps) 260 return err 261 }