github.com/core-coin/go-core/v2@v2.1.9/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/core-coin/go-core/v2/log" 11 "github.com/core-coin/go-core/v2/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 ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) 167 pts = append(pts, client.Point{ 168 Measurement: fmt.Sprintf("%s%s.histogram", namespace, name), 169 Tags: r.tags, 170 Fields: map[string]interface{}{ 171 "count": ms.Count(), 172 "max": ms.Max(), 173 "mean": ms.Mean(), 174 "min": ms.Min(), 175 "stddev": ms.StdDev(), 176 "variance": ms.Variance(), 177 "p50": ps[0], 178 "p75": ps[1], 179 "p95": ps[2], 180 "p99": ps[3], 181 "p999": ps[4], 182 "p9999": ps[5], 183 }, 184 Time: now, 185 }) 186 case metrics.Meter: 187 ms := metric.Snapshot() 188 pts = append(pts, client.Point{ 189 Measurement: fmt.Sprintf("%s%s.meter", namespace, name), 190 Tags: r.tags, 191 Fields: map[string]interface{}{ 192 "count": ms.Count(), 193 "m1": ms.Rate1(), 194 "m5": ms.Rate5(), 195 "m15": ms.Rate15(), 196 "mean": ms.RateMean(), 197 }, 198 Time: now, 199 }) 200 case metrics.Timer: 201 ms := metric.Snapshot() 202 ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) 203 pts = append(pts, client.Point{ 204 Measurement: fmt.Sprintf("%s%s.timer", namespace, name), 205 Tags: r.tags, 206 Fields: map[string]interface{}{ 207 "count": ms.Count(), 208 "max": ms.Max(), 209 "mean": ms.Mean(), 210 "min": ms.Min(), 211 "stddev": ms.StdDev(), 212 "variance": ms.Variance(), 213 "p50": ps[0], 214 "p75": ps[1], 215 "p95": ps[2], 216 "p99": ps[3], 217 "p999": ps[4], 218 "p9999": ps[5], 219 "m1": ms.Rate1(), 220 "m5": ms.Rate5(), 221 "m15": ms.Rate15(), 222 "meanrate": ms.RateMean(), 223 }, 224 Time: now, 225 }) 226 case metrics.ResettingTimer: 227 t := metric.Snapshot() 228 229 if len(t.Values()) > 0 { 230 ps := t.Percentiles([]float64{50, 95, 99}) 231 val := t.Values() 232 pts = append(pts, client.Point{ 233 Measurement: fmt.Sprintf("%s%s.span", namespace, name), 234 Tags: r.tags, 235 Fields: map[string]interface{}{ 236 "count": len(val), 237 "max": val[len(val)-1], 238 "mean": t.Mean(), 239 "min": val[0], 240 "p50": ps[0], 241 "p95": ps[1], 242 "p99": ps[2], 243 }, 244 Time: now, 245 }) 246 } 247 } 248 }) 249 250 bps := client.BatchPoints{ 251 Points: pts, 252 Database: r.database, 253 } 254 255 _, err := r.client.Write(bps) 256 return err 257 }