github.com/ebceco/ebc@v1.8.19-0.20190309150932-8cb0b9e06484/metrics/influxdb/influxdb.go (about)

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