github.com/tirogen/go-ethereum@v1.10.12-0.20221226051715-250cfede41b6/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  	"github.com/tirogen/go-ethereum/log"
    10  	"github.com/tirogen/go-ethereum/metrics"
    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.NewTicker(r.interval)
   102  	pingTicker := time.NewTicker(time.Second * 5)
   103  
   104  	for {
   105  		select {
   106  		case <-intervalTicker.C:
   107  			if err := r.send(); err != nil {
   108  				log.Warn("Unable to send to InfluxDB", "err", err)
   109  			}
   110  		case <-pingTicker.C:
   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  			count := metric.Count()
   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": count,
   138  				},
   139  				Time: now,
   140  			})
   141  		case metrics.Gauge:
   142  			ms := metric.Snapshot()
   143  			pts = append(pts, client.Point{
   144  				Measurement: fmt.Sprintf("%s%s.gauge", namespace, name),
   145  				Tags:        r.tags,
   146  				Fields: map[string]interface{}{
   147  					"value": ms.Value(),
   148  				},
   149  				Time: now,
   150  			})
   151  		case metrics.GaugeFloat64:
   152  			ms := metric.Snapshot()
   153  			pts = append(pts, client.Point{
   154  				Measurement: fmt.Sprintf("%s%s.gauge", namespace, name),
   155  				Tags:        r.tags,
   156  				Fields: map[string]interface{}{
   157  					"value": ms.Value(),
   158  				},
   159  				Time: now,
   160  			})
   161  		case metrics.Histogram:
   162  			ms := metric.Snapshot()
   163  			if ms.Count() > 0 {
   164  				ps := ms.Percentiles([]float64{0.25, 0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
   165  				fields := map[string]interface{}{
   166  					"count":    ms.Count(),
   167  					"max":      ms.Max(),
   168  					"mean":     ms.Mean(),
   169  					"min":      ms.Min(),
   170  					"stddev":   ms.StdDev(),
   171  					"variance": ms.Variance(),
   172  					"p25":      ps[0],
   173  					"p50":      ps[1],
   174  					"p75":      ps[2],
   175  					"p95":      ps[3],
   176  					"p99":      ps[4],
   177  					"p999":     ps[5],
   178  					"p9999":    ps[6],
   179  				}
   180  				pts = append(pts, client.Point{
   181  					Measurement: fmt.Sprintf("%s%s.histogram", namespace, name),
   182  					Tags:        r.tags,
   183  					Fields:      fields,
   184  					Time:        now,
   185  				})
   186  			}
   187  		case metrics.Meter:
   188  			ms := metric.Snapshot()
   189  			pts = append(pts, client.Point{
   190  				Measurement: fmt.Sprintf("%s%s.meter", namespace, name),
   191  				Tags:        r.tags,
   192  				Fields: map[string]interface{}{
   193  					"count": ms.Count(),
   194  					"m1":    ms.Rate1(),
   195  					"m5":    ms.Rate5(),
   196  					"m15":   ms.Rate15(),
   197  					"mean":  ms.RateMean(),
   198  				},
   199  				Time: now,
   200  			})
   201  		case metrics.Timer:
   202  			ms := metric.Snapshot()
   203  			ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
   204  			pts = append(pts, client.Point{
   205  				Measurement: fmt.Sprintf("%s%s.timer", namespace, name),
   206  				Tags:        r.tags,
   207  				Fields: map[string]interface{}{
   208  					"count":    ms.Count(),
   209  					"max":      ms.Max(),
   210  					"mean":     ms.Mean(),
   211  					"min":      ms.Min(),
   212  					"stddev":   ms.StdDev(),
   213  					"variance": ms.Variance(),
   214  					"p50":      ps[0],
   215  					"p75":      ps[1],
   216  					"p95":      ps[2],
   217  					"p99":      ps[3],
   218  					"p999":     ps[4],
   219  					"p9999":    ps[5],
   220  					"m1":       ms.Rate1(),
   221  					"m5":       ms.Rate5(),
   222  					"m15":      ms.Rate15(),
   223  					"meanrate": ms.RateMean(),
   224  				},
   225  				Time: now,
   226  			})
   227  		case metrics.ResettingTimer:
   228  			t := metric.Snapshot()
   229  
   230  			if len(t.Values()) > 0 {
   231  				ps := t.Percentiles([]float64{50, 95, 99})
   232  				val := t.Values()
   233  				pts = append(pts, client.Point{
   234  					Measurement: fmt.Sprintf("%s%s.span", namespace, name),
   235  					Tags:        r.tags,
   236  					Fields: map[string]interface{}{
   237  						"count": len(val),
   238  						"max":   val[len(val)-1],
   239  						"mean":  t.Mean(),
   240  						"min":   val[0],
   241  						"p50":   ps[0],
   242  						"p95":   ps[1],
   243  						"p99":   ps[2],
   244  					},
   245  					Time: now,
   246  				})
   247  			}
   248  		}
   249  	})
   250  
   251  	bps := client.BatchPoints{
   252  		Points:   pts,
   253  		Database: r.database,
   254  	}
   255  
   256  	_, err := r.client.Write(bps)
   257  	return err
   258  }