github.com/tacshi/go-ethereum@v0.0.0-20230616113857-84a434e20921/metrics/influxdb/influxdbv2.go (about) 1 package influxdb 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 influxdb2 "github.com/influxdata/influxdb-client-go/v2" 9 "github.com/influxdata/influxdb-client-go/v2/api" 10 "github.com/tacshi/go-ethereum/log" 11 "github.com/tacshi/go-ethereum/metrics" 12 ) 13 14 type v2Reporter struct { 15 reg metrics.Registry 16 interval time.Duration 17 18 endpoint string 19 token string 20 bucket string 21 organization string 22 namespace string 23 tags map[string]string 24 25 client influxdb2.Client 26 write api.WriteAPI 27 28 cache map[string]int64 29 } 30 31 // InfluxDBWithTags starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval with the specified tags 32 func InfluxDBV2WithTags(r metrics.Registry, d time.Duration, endpoint string, token string, bucket string, organization string, namespace string, tags map[string]string) { 33 rep := &v2Reporter{ 34 reg: r, 35 interval: d, 36 endpoint: endpoint, 37 token: token, 38 bucket: bucket, 39 organization: organization, 40 namespace: namespace, 41 tags: tags, 42 cache: make(map[string]int64), 43 } 44 45 rep.client = influxdb2.NewClient(rep.endpoint, rep.token) 46 defer rep.client.Close() 47 48 // async write client 49 rep.write = rep.client.WriteAPI(rep.organization, rep.bucket) 50 errorsCh := rep.write.Errors() 51 52 // have to handle write errors in a separate goroutine like this b/c the channel is unbuffered and will block writes if not read 53 go func() { 54 for err := range errorsCh { 55 log.Warn("write error", "err", err.Error()) 56 } 57 }() 58 rep.run() 59 } 60 61 func (r *v2Reporter) run() { 62 intervalTicker := time.NewTicker(r.interval) 63 pingTicker := time.NewTicker(time.Second * 5) 64 65 defer intervalTicker.Stop() 66 defer pingTicker.Stop() 67 68 for { 69 select { 70 case <-intervalTicker.C: 71 r.send() 72 case <-pingTicker.C: 73 _, err := r.client.Health(context.Background()) 74 if err != nil { 75 log.Warn("Got error from influxdb client health check", "err", err.Error()) 76 } 77 } 78 } 79 } 80 81 func (r *v2Reporter) send() { 82 r.reg.Each(func(name string, i interface{}) { 83 now := time.Now() 84 namespace := r.namespace 85 86 switch metric := i.(type) { 87 case metrics.Counter: 88 v := metric.Count() 89 l := r.cache[name] 90 91 measurement := fmt.Sprintf("%s%s.count", namespace, name) 92 fields := map[string]interface{}{ 93 "value": v - l, 94 } 95 96 pt := influxdb2.NewPoint(measurement, r.tags, fields, now) 97 r.write.WritePoint(pt) 98 99 r.cache[name] = v 100 101 case metrics.Gauge: 102 ms := metric.Snapshot() 103 104 measurement := fmt.Sprintf("%s%s.gauge", namespace, name) 105 fields := map[string]interface{}{ 106 "value": ms.Value(), 107 } 108 109 pt := influxdb2.NewPoint(measurement, r.tags, fields, now) 110 r.write.WritePoint(pt) 111 112 case metrics.GaugeFloat64: 113 ms := metric.Snapshot() 114 115 measurement := fmt.Sprintf("%s%s.gauge", namespace, name) 116 fields := map[string]interface{}{ 117 "value": ms.Value(), 118 } 119 120 pt := influxdb2.NewPoint(measurement, r.tags, fields, now) 121 r.write.WritePoint(pt) 122 123 case metrics.Histogram: 124 ms := metric.Snapshot() 125 126 if ms.Count() > 0 { 127 ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) 128 measurement := fmt.Sprintf("%s%s.histogram", namespace, name) 129 fields := map[string]interface{}{ 130 "count": ms.Count(), 131 "max": ms.Max(), 132 "mean": ms.Mean(), 133 "min": ms.Min(), 134 "stddev": ms.StdDev(), 135 "variance": ms.Variance(), 136 "p50": ps[0], 137 "p75": ps[1], 138 "p95": ps[2], 139 "p99": ps[3], 140 "p999": ps[4], 141 "p9999": ps[5], 142 } 143 144 pt := influxdb2.NewPoint(measurement, r.tags, fields, now) 145 r.write.WritePoint(pt) 146 } 147 148 case metrics.Meter: 149 ms := metric.Snapshot() 150 151 measurement := fmt.Sprintf("%s%s.meter", namespace, name) 152 fields := map[string]interface{}{ 153 "count": ms.Count(), 154 "m1": ms.Rate1(), 155 "m5": ms.Rate5(), 156 "m15": ms.Rate15(), 157 "mean": ms.RateMean(), 158 } 159 160 pt := influxdb2.NewPoint(measurement, r.tags, fields, now) 161 r.write.WritePoint(pt) 162 163 case metrics.Timer: 164 ms := metric.Snapshot() 165 ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) 166 167 measurement := fmt.Sprintf("%s%s.timer", namespace, name) 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 "p50": ps[0], 176 "p75": ps[1], 177 "p95": ps[2], 178 "p99": ps[3], 179 "p999": ps[4], 180 "p9999": ps[5], 181 "m1": ms.Rate1(), 182 "m5": ms.Rate5(), 183 "m15": ms.Rate15(), 184 "meanrate": ms.RateMean(), 185 } 186 187 pt := influxdb2.NewPoint(measurement, r.tags, fields, now) 188 r.write.WritePoint(pt) 189 190 case metrics.ResettingTimer: 191 t := metric.Snapshot() 192 193 if len(t.Values()) > 0 { 194 ps := t.Percentiles([]float64{50, 95, 99}) 195 val := t.Values() 196 197 measurement := fmt.Sprintf("%s%s.span", namespace, name) 198 fields := map[string]interface{}{ 199 "count": len(val), 200 "max": val[len(val)-1], 201 "mean": t.Mean(), 202 "min": val[0], 203 "p50": ps[0], 204 "p95": ps[1], 205 "p99": ps[2], 206 } 207 208 pt := influxdb2.NewPoint(measurement, r.tags, fields, now) 209 r.write.WritePoint(pt) 210 } 211 } 212 }) 213 214 // Force all unwritten data to be sent 215 r.write.Flush() 216 }