github.com/mprishchepo/go-ethereum@v1.9.7-0.20191031044858-21506be82b68/metrics/influxdb/influxdb.go (about)

     1  package influxdb
     2  
     3  import (
     4  	"fmt"
     5  	uurl "net/url"
     6  	"time"
     7  
     8  	"github.com/Fantom-foundation/go-ethereum/log"
     9  	"github.com/Fantom-foundation/go-ethereum/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  		Timeout:  10 * time.Second,
    95  	})
    96  
    97  	return
    98  }
    99  
   100  func (r *reporter) run() {
   101  	intervalTicker := time.Tick(r.interval)
   102  	pingTicker := time.Tick(time.Second * 5)
   103  
   104  	for {
   105  		select {
   106  		case <-intervalTicker:
   107  			if err := r.send(); err != nil {
   108  				log.Warn("Unable to send to InfluxDB", "err", err)
   109  			}
   110  		case <-pingTicker:
   111  			_, _, err := r.client.Ping()
   112  			if err != nil {
   113  				log.Warn("Got error while sending a ping to InfluxDB, trying to recreate client", "err", err)
   114  
   115  				if err = r.makeClient(); err != nil {
   116  					log.Warn("Unable to make InfluxDB client", "err", err)
   117  				}
   118  			}
   119  		}
   120  	}
   121  }
   122  
   123  func (r *reporter) send() error {
   124  	var pts []client.Point
   125  
   126  	r.reg.Each(func(name string, i interface{}) {
   127  		now := time.Now()
   128  		namespace := r.namespace
   129  
   130  		switch metric := i.(type) {
   131  		case metrics.Counter:
   132  			v := metric.Count()
   133  			l := r.cache[name]
   134  			pts = append(pts, client.Point{
   135  				Measurement: fmt.Sprintf("%s%s.count", namespace, name),
   136  				Tags:        r.tags,
   137  				Fields: map[string]interface{}{
   138  					"value": v - l,
   139  				},
   140  				Time: now,
   141  			})
   142  			r.cache[name] = v
   143  		case metrics.Gauge:
   144  			ms := metric.Snapshot()
   145  			pts = append(pts, client.Point{
   146  				Measurement: fmt.Sprintf("%s%s.gauge", namespace, name),
   147  				Tags:        r.tags,
   148  				Fields: map[string]interface{}{
   149  					"value": ms.Value(),
   150  				},
   151  				Time: now,
   152  			})
   153  		case metrics.GaugeFloat64:
   154  			ms := metric.Snapshot()
   155  			pts = append(pts, client.Point{
   156  				Measurement: fmt.Sprintf("%s%s.gauge", namespace, name),
   157  				Tags:        r.tags,
   158  				Fields: map[string]interface{}{
   159  					"value": ms.Value(),
   160  				},
   161  				Time: now,
   162  			})
   163  		case metrics.Histogram:
   164  			ms := metric.Snapshot()
   165  			ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
   166  			pts = append(pts, client.Point{
   167  				Measurement: fmt.Sprintf("%s%s.histogram", namespace, name),
   168  				Tags:        r.tags,
   169  				Fields: map[string]interface{}{
   170  					"count":    ms.Count(),
   171  					"max":      ms.Max(),
   172  					"mean":     ms.Mean(),
   173  					"min":      ms.Min(),
   174  					"stddev":   ms.StdDev(),
   175  					"variance": ms.Variance(),
   176  					"p50":      ps[0],
   177  					"p75":      ps[1],
   178  					"p95":      ps[2],
   179  					"p99":      ps[3],
   180  					"p999":     ps[4],
   181  					"p9999":    ps[5],
   182  				},
   183  				Time: now,
   184  			})
   185  		case metrics.Meter:
   186  			ms := metric.Snapshot()
   187  			pts = append(pts, client.Point{
   188  				Measurement: fmt.Sprintf("%s%s.meter", namespace, name),
   189  				Tags:        r.tags,
   190  				Fields: map[string]interface{}{
   191  					"count": ms.Count(),
   192  					"m1":    ms.Rate1(),
   193  					"m5":    ms.Rate5(),
   194  					"m15":   ms.Rate15(),
   195  					"mean":  ms.RateMean(),
   196  				},
   197  				Time: now,
   198  			})
   199  		case metrics.Timer:
   200  			ms := metric.Snapshot()
   201  			ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
   202  			pts = append(pts, client.Point{
   203  				Measurement: fmt.Sprintf("%s%s.timer", namespace, name),
   204  				Tags:        r.tags,
   205  				Fields: map[string]interface{}{
   206  					"count":    ms.Count(),
   207  					"max":      ms.Max(),
   208  					"mean":     ms.Mean(),
   209  					"min":      ms.Min(),
   210  					"stddev":   ms.StdDev(),
   211  					"variance": ms.Variance(),
   212  					"p50":      ps[0],
   213  					"p75":      ps[1],
   214  					"p95":      ps[2],
   215  					"p99":      ps[3],
   216  					"p999":     ps[4],
   217  					"p9999":    ps[5],
   218  					"m1":       ms.Rate1(),
   219  					"m5":       ms.Rate5(),
   220  					"m15":      ms.Rate15(),
   221  					"meanrate": ms.RateMean(),
   222  				},
   223  				Time: now,
   224  			})
   225  		case metrics.ResettingTimer:
   226  			t := metric.Snapshot()
   227  
   228  			if len(t.Values()) > 0 {
   229  				ps := t.Percentiles([]float64{50, 95, 99})
   230  				val := t.Values()
   231  				pts = append(pts, client.Point{
   232  					Measurement: fmt.Sprintf("%s%s.span", namespace, name),
   233  					Tags:        r.tags,
   234  					Fields: map[string]interface{}{
   235  						"count": len(val),
   236  						"max":   val[len(val)-1],
   237  						"mean":  t.Mean(),
   238  						"min":   val[0],
   239  						"p50":   ps[0],
   240  						"p95":   ps[1],
   241  						"p99":   ps[2],
   242  					},
   243  					Time: now,
   244  				})
   245  			}
   246  		}
   247  	})
   248  
   249  	bps := client.BatchPoints{
   250  		Points:   pts,
   251  		Database: r.database,
   252  	}
   253  
   254  	_, err := r.client.Write(bps)
   255  	return err
   256  }