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  }