github.com/tirogen/go-ethereum@v1.10.12-0.20221226051715-250cfede41b6/metrics/influxdb/influxdbv2.go (about)

     1  package influxdb
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	influxdb2 "github.com/influxdata/influxdb-client-go/v2"
     9  	"github.com/influxdata/influxdb-client-go/v2/api"
    10  	"github.com/tirogen/go-ethereum/log"
    11  	"github.com/tirogen/go-ethereum/metrics"
    12  )
    13  
    14  type v2Reporter struct {
    15  	reg      metrics.Registry
    16  	interval time.Duration
    17  
    18  	endpoint     string
    19  	token        string
    20  	bucket       string
    21  	organization string
    22  	namespace    string
    23  	tags         map[string]string
    24  
    25  	client influxdb2.Client
    26  	write  api.WriteAPI
    27  
    28  	cache map[string]int64
    29  }
    30  
    31  // InfluxDBWithTags starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval with the specified tags
    32  func InfluxDBV2WithTags(r metrics.Registry, d time.Duration, endpoint string, token string, bucket string, organization string, namespace string, tags map[string]string) {
    33  	rep := &v2Reporter{
    34  		reg:          r,
    35  		interval:     d,
    36  		endpoint:     endpoint,
    37  		token:        token,
    38  		bucket:       bucket,
    39  		organization: organization,
    40  		namespace:    namespace,
    41  		tags:         tags,
    42  		cache:        make(map[string]int64),
    43  	}
    44  
    45  	rep.client = influxdb2.NewClient(rep.endpoint, rep.token)
    46  	defer rep.client.Close()
    47  
    48  	// async write client
    49  	rep.write = rep.client.WriteAPI(rep.organization, rep.bucket)
    50  	errorsCh := rep.write.Errors()
    51  
    52  	// have to handle write errors in a separate goroutine like this b/c the channel is unbuffered and will block writes if not read
    53  	go func() {
    54  		for err := range errorsCh {
    55  			log.Warn("write error", "err", err.Error())
    56  		}
    57  	}()
    58  	rep.run()
    59  }
    60  
    61  func (r *v2Reporter) run() {
    62  	intervalTicker := time.NewTicker(r.interval)
    63  	pingTicker := time.NewTicker(time.Second * 5)
    64  
    65  	for {
    66  		select {
    67  		case <-intervalTicker.C:
    68  			r.send()
    69  		case <-pingTicker.C:
    70  			_, err := r.client.Health(context.Background())
    71  			if err != nil {
    72  				log.Warn("Got error from influxdb client health check", "err", err.Error())
    73  			}
    74  		}
    75  	}
    76  }
    77  
    78  func (r *v2Reporter) send() {
    79  	r.reg.Each(func(name string, i interface{}) {
    80  		now := time.Now()
    81  		namespace := r.namespace
    82  
    83  		switch metric := i.(type) {
    84  		case metrics.Counter:
    85  			v := metric.Count()
    86  			l := r.cache[name]
    87  
    88  			measurement := fmt.Sprintf("%s%s.count", namespace, name)
    89  			fields := map[string]interface{}{
    90  				"value": v - l,
    91  			}
    92  
    93  			pt := influxdb2.NewPoint(measurement, r.tags, fields, now)
    94  			r.write.WritePoint(pt)
    95  
    96  			r.cache[name] = v
    97  
    98  		case metrics.Gauge:
    99  			ms := metric.Snapshot()
   100  
   101  			measurement := fmt.Sprintf("%s%s.gauge", namespace, name)
   102  			fields := map[string]interface{}{
   103  				"value": ms.Value(),
   104  			}
   105  
   106  			pt := influxdb2.NewPoint(measurement, r.tags, fields, now)
   107  			r.write.WritePoint(pt)
   108  
   109  		case metrics.GaugeFloat64:
   110  			ms := metric.Snapshot()
   111  
   112  			measurement := fmt.Sprintf("%s%s.gauge", namespace, name)
   113  			fields := map[string]interface{}{
   114  				"value": ms.Value(),
   115  			}
   116  
   117  			pt := influxdb2.NewPoint(measurement, r.tags, fields, now)
   118  			r.write.WritePoint(pt)
   119  
   120  		case metrics.Histogram:
   121  			ms := metric.Snapshot()
   122  
   123  			if ms.Count() > 0 {
   124  				ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
   125  				measurement := fmt.Sprintf("%s%s.histogram", namespace, name)
   126  				fields := map[string]interface{}{
   127  					"count":    ms.Count(),
   128  					"max":      ms.Max(),
   129  					"mean":     ms.Mean(),
   130  					"min":      ms.Min(),
   131  					"stddev":   ms.StdDev(),
   132  					"variance": ms.Variance(),
   133  					"p50":      ps[0],
   134  					"p75":      ps[1],
   135  					"p95":      ps[2],
   136  					"p99":      ps[3],
   137  					"p999":     ps[4],
   138  					"p9999":    ps[5],
   139  				}
   140  
   141  				pt := influxdb2.NewPoint(measurement, r.tags, fields, now)
   142  				r.write.WritePoint(pt)
   143  			}
   144  
   145  		case metrics.Meter:
   146  			ms := metric.Snapshot()
   147  
   148  			measurement := fmt.Sprintf("%s%s.meter", namespace, name)
   149  			fields := map[string]interface{}{
   150  				"count": ms.Count(),
   151  				"m1":    ms.Rate1(),
   152  				"m5":    ms.Rate5(),
   153  				"m15":   ms.Rate15(),
   154  				"mean":  ms.RateMean(),
   155  			}
   156  
   157  			pt := influxdb2.NewPoint(measurement, r.tags, fields, now)
   158  			r.write.WritePoint(pt)
   159  
   160  		case metrics.Timer:
   161  			ms := metric.Snapshot()
   162  			ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
   163  
   164  			measurement := fmt.Sprintf("%s%s.timer", namespace, name)
   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  				"p50":      ps[0],
   173  				"p75":      ps[1],
   174  				"p95":      ps[2],
   175  				"p99":      ps[3],
   176  				"p999":     ps[4],
   177  				"p9999":    ps[5],
   178  				"m1":       ms.Rate1(),
   179  				"m5":       ms.Rate5(),
   180  				"m15":      ms.Rate15(),
   181  				"meanrate": ms.RateMean(),
   182  			}
   183  
   184  			pt := influxdb2.NewPoint(measurement, r.tags, fields, now)
   185  			r.write.WritePoint(pt)
   186  
   187  		case metrics.ResettingTimer:
   188  			t := metric.Snapshot()
   189  
   190  			if len(t.Values()) > 0 {
   191  				ps := t.Percentiles([]float64{50, 95, 99})
   192  				val := t.Values()
   193  
   194  				measurement := fmt.Sprintf("%s%s.span", namespace, name)
   195  				fields := map[string]interface{}{
   196  					"count": len(val),
   197  					"max":   val[len(val)-1],
   198  					"mean":  t.Mean(),
   199  					"min":   val[0],
   200  					"p50":   ps[0],
   201  					"p95":   ps[1],
   202  					"p99":   ps[2],
   203  				}
   204  
   205  				pt := influxdb2.NewPoint(measurement, r.tags, fields, now)
   206  				r.write.WritePoint(pt)
   207  			}
   208  		}
   209  	})
   210  
   211  	// Force all unwritten data to be sent
   212  	r.write.Flush()
   213  }