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