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  }