github.com/tacshi/go-ethereum@v0.0.0-20230616113857-84a434e20921/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/tacshi/go-ethereum/log"
    10  	"github.com/tacshi/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  	defer intervalTicker.Stop()
   105  	defer pingTicker.Stop()
   106  
   107  	for {
   108  		select {
   109  		case <-intervalTicker.C:
   110  			if err := r.send(); err != nil {
   111  				log.Warn("Unable to send to InfluxDB", "err", err)
   112  			}
   113  		case <-pingTicker.C:
   114  			_, _, err := r.client.Ping()
   115  			if err != nil {
   116  				log.Warn("Got error while sending a ping to InfluxDB, trying to recreate client", "err", err)
   117  
   118  				if err = r.makeClient(); err != nil {
   119  					log.Warn("Unable to make InfluxDB client", "err", err)
   120  				}
   121  			}
   122  		}
   123  	}
   124  }
   125  
   126  func (r *reporter) send() error {
   127  	var pts []client.Point
   128  
   129  	r.reg.Each(func(name string, i interface{}) {
   130  		now := time.Now()
   131  		namespace := r.namespace
   132  
   133  		switch metric := i.(type) {
   134  		case metrics.Counter:
   135  			count := metric.Count()
   136  			pts = append(pts, client.Point{
   137  				Measurement: fmt.Sprintf("%s%s.count", namespace, name),
   138  				Tags:        r.tags,
   139  				Fields: map[string]interface{}{
   140  					"value": count,
   141  				},
   142  				Time: now,
   143  			})
   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  			if ms.Count() > 0 {
   167  				ps := ms.Percentiles([]float64{0.25, 0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
   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  					"p25":      ps[0],
   176  					"p50":      ps[1],
   177  					"p75":      ps[2],
   178  					"p95":      ps[3],
   179  					"p99":      ps[4],
   180  					"p999":     ps[5],
   181  					"p9999":    ps[6],
   182  				}
   183  				pts = append(pts, client.Point{
   184  					Measurement: fmt.Sprintf("%s%s.histogram", namespace, name),
   185  					Tags:        r.tags,
   186  					Fields:      fields,
   187  					Time:        now,
   188  				})
   189  			}
   190  		case metrics.Meter:
   191  			ms := metric.Snapshot()
   192  			pts = append(pts, client.Point{
   193  				Measurement: fmt.Sprintf("%s%s.meter", namespace, name),
   194  				Tags:        r.tags,
   195  				Fields: map[string]interface{}{
   196  					"count": ms.Count(),
   197  					"m1":    ms.Rate1(),
   198  					"m5":    ms.Rate5(),
   199  					"m15":   ms.Rate15(),
   200  					"mean":  ms.RateMean(),
   201  				},
   202  				Time: now,
   203  			})
   204  		case metrics.Timer:
   205  			ms := metric.Snapshot()
   206  			ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
   207  			pts = append(pts, client.Point{
   208  				Measurement: fmt.Sprintf("%s%s.timer", namespace, name),
   209  				Tags:        r.tags,
   210  				Fields: map[string]interface{}{
   211  					"count":    ms.Count(),
   212  					"max":      ms.Max(),
   213  					"mean":     ms.Mean(),
   214  					"min":      ms.Min(),
   215  					"stddev":   ms.StdDev(),
   216  					"variance": ms.Variance(),
   217  					"p50":      ps[0],
   218  					"p75":      ps[1],
   219  					"p95":      ps[2],
   220  					"p99":      ps[3],
   221  					"p999":     ps[4],
   222  					"p9999":    ps[5],
   223  					"m1":       ms.Rate1(),
   224  					"m5":       ms.Rate5(),
   225  					"m15":      ms.Rate15(),
   226  					"meanrate": ms.RateMean(),
   227  				},
   228  				Time: now,
   229  			})
   230  		case metrics.ResettingTimer:
   231  			t := metric.Snapshot()
   232  
   233  			if len(t.Values()) > 0 {
   234  				ps := t.Percentiles([]float64{50, 95, 99})
   235  				val := t.Values()
   236  				pts = append(pts, client.Point{
   237  					Measurement: fmt.Sprintf("%s%s.span", namespace, name),
   238  					Tags:        r.tags,
   239  					Fields: map[string]interface{}{
   240  						"count": len(val),
   241  						"max":   val[len(val)-1],
   242  						"mean":  t.Mean(),
   243  						"min":   val[0],
   244  						"p50":   ps[0],
   245  						"p95":   ps[1],
   246  						"p99":   ps[2],
   247  					},
   248  					Time: now,
   249  				})
   250  			}
   251  		}
   252  	})
   253  
   254  	bps := client.BatchPoints{
   255  		Points:   pts,
   256  		Database: r.database,
   257  	}
   258  
   259  	_, err := r.client.Write(bps)
   260  	return err
   261  }