github.com/sereiner/library@v0.0.0-20200518095232-1fa3e640cc5f/metrics/influxdb.go (about) 1 package metrics 2 3 import ( 4 "fmt" 5 uurl "net/url" 6 "strings" 7 "time" 8 9 "github.com/sereiner/library/influxdb" 10 logger "github.com/sereiner/library/log" 11 "github.com/zkfy/cron" 12 "github.com/zkfy/go-metrics" 13 ) 14 15 type IReporter interface { 16 Close() error 17 Run() 18 } 19 type reporter struct { 20 reg metrics.Registry 21 schedule cron.Schedule 22 cron string 23 url uurl.URL 24 database string 25 username string 26 password string 27 tags map[string]string 28 client *influxdb.Client 29 logger *logger.Logger 30 done bool 31 } 32 33 const ( 34 WORKING = "working" 35 COUNTER = "counter" 36 GAUGE = "gauge" 37 GAUGEFLOAST64 = "gaugeFloat64" 38 HISTOGRAM = "histogram" 39 METER = "meter" 40 TIMER = "timer" 41 QPS = "qps" 42 ) 43 44 // InfluxDB starts a InfluxDB reporter which will post the metrics from the given registry at each d interval. 45 func InfluxDB(r metrics.Registry, cron string, url, database, username, password string, logger *logger.Logger) (IReporter, error) { 46 return InfluxDBWithTags(r, cron, url, database, username, password, nil, logger) 47 } 48 49 //MakeName 构建参数名称 50 func MakeName(name string, tp string, params ...string) string { 51 if len(params)%2 != 0 { 52 panic("MakeName params必须成对输入") 53 } 54 return name + "." + tp + ":>" + strings.Join(params, ":>") 55 } 56 57 //timer.merchant.api.request-server-192.168.0.240-client-127.0.0.1-url-/colin 58 func splitGroup(name string) (string, map[string]string) { 59 names := strings.Split(name, ":>") 60 tags := make(map[string]string) 61 count := len(names) 62 for i := 1; i < count; i++ { 63 if i%2 == 1 && i+1 < count { 64 tags[names[i]] = names[i+1] 65 } 66 } 67 return names[0], tags 68 } 69 70 // InfluxDBWithTags starts a InfluxDB reporter which will post the metrics from the given registry at each d interval with the specified tags 71 func InfluxDBWithTags(r metrics.Registry, c string, url, database, username, password string, tags map[string]string, logger *logger.Logger) (IReporter, error) { 72 sch, err := cron.ParseStandard(c) 73 if err != nil { 74 return nil, err 75 } 76 77 u, err := uurl.Parse(url) 78 if err != nil { 79 return nil, fmt.Errorf("unable to parse InfluxDB url %s. err=%v", url, err) 80 } 81 82 rep := &reporter{ 83 logger: logger, 84 cron: c, 85 reg: r, 86 schedule: sch, 87 url: *u, 88 database: database, 89 username: username, 90 password: password, 91 tags: tags, 92 } 93 if err := rep.makeClient(); err != nil { 94 return nil, fmt.Errorf("unable to make InfluxDB client. err=%v", err) 95 } 96 97 return rep, nil 98 } 99 func (r *reporter) Run() { 100 r.run() 101 } 102 func (r *reporter) makeClient() (err error) { 103 r.client, err = influxdb.NewClient(influxdb.Config{ 104 URL: r.url, 105 Username: r.username, 106 Password: r.password, 107 }) 108 109 return 110 } 111 112 func (r *reporter) run() { 113 pingTicker := time.Tick(time.Second * 5) 114 var intervalTicker int64 115 LOOP: 116 for { 117 select { 118 case <-time.After(time.Second): 119 if r.done { 120 break LOOP 121 } 122 now := time.Now() 123 if intervalTicker > now.Unix() { 124 break 125 } 126 go func() { 127 if err := r.send(); err != nil { 128 r.logger.Errorf("unable to send metrics to InfluxDB. err=%v", err) 129 } 130 }() 131 intervalTicker = r.schedule.Next(now).Unix() 132 case <-pingTicker: 133 _, _, err := r.client.Ping() 134 if err != nil { 135 r.logger.Errorf("got error while sending a ping to InfluxDB, trying to recreate client. err=%v", err) 136 137 if err = r.makeClient(); err != nil { 138 r.logger.Errorf("unable to make InfluxDB client. err=%v", err) 139 } 140 } 141 } 142 } 143 } 144 145 func (r *reporter) send() error { 146 var pts []influxdb.Point 147 r.reg.Each(func(name string, obj interface{}) { 148 now := time.Now() 149 rname, tags := splitGroup(name) 150 switch metric := obj.(type) { 151 case IQPS: 152 metric.Mark(0) 153 pts = append(pts, influxdb.Point{ 154 Measurement: rname, 155 Tags: tags, 156 Fields: map[string]interface{}{ 157 "m1": metric.M1(), 158 "m5": metric.M5(), 159 "m15": metric.M15(), 160 }, 161 Time: now, 162 }) 163 case Counter: 164 ms := metric.Snapshot() 165 pts = append(pts, influxdb.Point{ 166 Measurement: rname, 167 Tags: tags, 168 Fields: map[string]interface{}{ 169 "value": ms.Count(), 170 }, 171 Time: now, 172 }) 173 case Gauge: 174 ms := metric.Snapshot() 175 pts = append(pts, influxdb.Point{ 176 Measurement: rname, 177 Tags: tags, 178 Fields: map[string]interface{}{ 179 "value": ms.Value(), 180 }, 181 Time: now, 182 }) 183 case GaugeFloat64: 184 ms := metric.Snapshot() 185 pts = append(pts, influxdb.Point{ 186 Measurement: rname, 187 Tags: tags, 188 Fields: map[string]interface{}{ 189 "value": ms.Value(), 190 }, 191 Time: now, 192 }) 193 case Histogram: 194 ms := metric.Snapshot() 195 ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) 196 pts = append(pts, influxdb.Point{ 197 Measurement: rname, 198 Tags: tags, 199 Fields: map[string]interface{}{ 200 "count": ms.Count(), 201 "max": ms.Max(), 202 "mean": ms.Mean(), 203 "min": ms.Min(), 204 "stddev": ms.StdDev(), 205 "variance": ms.Variance(), 206 "p50": ps[0], 207 "p75": ps[1], 208 "p95": ps[2], 209 "p99": ps[3], 210 "p999": ps[4], 211 "p9999": ps[5], 212 }, 213 Time: now, 214 }) 215 case Meter: 216 ms := metric.Snapshot() 217 pts = append(pts, influxdb.Point{ 218 Measurement: rname, 219 Tags: tags, 220 Fields: map[string]interface{}{ 221 "count": ms.Count(), 222 "m1": ms.Rate1(), 223 "m5": ms.Rate5(), 224 "m15": ms.Rate15(), 225 "mean": ms.RateMean(), 226 }, 227 Time: now, 228 }) 229 case Timer: 230 ms := metric.Snapshot() 231 ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) 232 pts = append(pts, influxdb.Point{ 233 Measurement: rname, 234 Tags: tags, 235 Fields: map[string]interface{}{ 236 "count": ms.Count(), 237 "max": ms.Max(), 238 "mean": ms.Mean(), 239 "min": ms.Min(), 240 "stddev": ms.StdDev(), 241 "variance": ms.Variance(), 242 "p50": ps[0], 243 "p75": ps[1], 244 "p95": ps[2], 245 "p99": ps[3], 246 "p999": ps[4], 247 "p9999": ps[5], 248 "m1": ms.Rate1(), 249 "m5": ms.Rate5(), 250 "m15": ms.Rate15(), 251 "meanrate": ms.RateMean(), 252 }, 253 Time: now, 254 }) 255 } 256 }) 257 258 bps := influxdb.BatchPoints{ 259 Points: pts, 260 Database: r.database, 261 } 262 _, err := r.client.Write(bps) 263 return err 264 } 265 func (r *reporter) Close() error { 266 r.done = true 267 return nil 268 }