github.com/tacshi/go-ethereum@v0.0.0-20230616113857-84a434e20921/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/tacshi/go-ethereum/log"
    11  	"github.com/tacshi/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  	defer intervalTicker.Stop()
    66  	defer pingTicker.Stop()
    67  
    68  	for {
    69  		select {
    70  		case <-intervalTicker.C:
    71  			r.send()
    72  		case <-pingTicker.C:
    73  			_, err := r.client.Health(context.Background())
    74  			if err != nil {
    75  				log.Warn("Got error from influxdb client health check", "err", err.Error())
    76  			}
    77  		}
    78  	}
    79  }
    80  
    81  func (r *v2Reporter) send() {
    82  	r.reg.Each(func(name string, i interface{}) {
    83  		now := time.Now()
    84  		namespace := r.namespace
    85  
    86  		switch metric := i.(type) {
    87  		case metrics.Counter:
    88  			v := metric.Count()
    89  			l := r.cache[name]
    90  
    91  			measurement := fmt.Sprintf("%s%s.count", namespace, name)
    92  			fields := map[string]interface{}{
    93  				"value": v - l,
    94  			}
    95  
    96  			pt := influxdb2.NewPoint(measurement, r.tags, fields, now)
    97  			r.write.WritePoint(pt)
    98  
    99  			r.cache[name] = v
   100  
   101  		case metrics.Gauge:
   102  			ms := metric.Snapshot()
   103  
   104  			measurement := fmt.Sprintf("%s%s.gauge", namespace, name)
   105  			fields := map[string]interface{}{
   106  				"value": ms.Value(),
   107  			}
   108  
   109  			pt := influxdb2.NewPoint(measurement, r.tags, fields, now)
   110  			r.write.WritePoint(pt)
   111  
   112  		case metrics.GaugeFloat64:
   113  			ms := metric.Snapshot()
   114  
   115  			measurement := fmt.Sprintf("%s%s.gauge", namespace, name)
   116  			fields := map[string]interface{}{
   117  				"value": ms.Value(),
   118  			}
   119  
   120  			pt := influxdb2.NewPoint(measurement, r.tags, fields, now)
   121  			r.write.WritePoint(pt)
   122  
   123  		case metrics.Histogram:
   124  			ms := metric.Snapshot()
   125  
   126  			if ms.Count() > 0 {
   127  				ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
   128  				measurement := fmt.Sprintf("%s%s.histogram", namespace, name)
   129  				fields := map[string]interface{}{
   130  					"count":    ms.Count(),
   131  					"max":      ms.Max(),
   132  					"mean":     ms.Mean(),
   133  					"min":      ms.Min(),
   134  					"stddev":   ms.StdDev(),
   135  					"variance": ms.Variance(),
   136  					"p50":      ps[0],
   137  					"p75":      ps[1],
   138  					"p95":      ps[2],
   139  					"p99":      ps[3],
   140  					"p999":     ps[4],
   141  					"p9999":    ps[5],
   142  				}
   143  
   144  				pt := influxdb2.NewPoint(measurement, r.tags, fields, now)
   145  				r.write.WritePoint(pt)
   146  			}
   147  
   148  		case metrics.Meter:
   149  			ms := metric.Snapshot()
   150  
   151  			measurement := fmt.Sprintf("%s%s.meter", namespace, name)
   152  			fields := map[string]interface{}{
   153  				"count": ms.Count(),
   154  				"m1":    ms.Rate1(),
   155  				"m5":    ms.Rate5(),
   156  				"m15":   ms.Rate15(),
   157  				"mean":  ms.RateMean(),
   158  			}
   159  
   160  			pt := influxdb2.NewPoint(measurement, r.tags, fields, now)
   161  			r.write.WritePoint(pt)
   162  
   163  		case metrics.Timer:
   164  			ms := metric.Snapshot()
   165  			ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
   166  
   167  			measurement := fmt.Sprintf("%s%s.timer", namespace, name)
   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  				"p50":      ps[0],
   176  				"p75":      ps[1],
   177  				"p95":      ps[2],
   178  				"p99":      ps[3],
   179  				"p999":     ps[4],
   180  				"p9999":    ps[5],
   181  				"m1":       ms.Rate1(),
   182  				"m5":       ms.Rate5(),
   183  				"m15":      ms.Rate15(),
   184  				"meanrate": ms.RateMean(),
   185  			}
   186  
   187  			pt := influxdb2.NewPoint(measurement, r.tags, fields, now)
   188  			r.write.WritePoint(pt)
   189  
   190  		case metrics.ResettingTimer:
   191  			t := metric.Snapshot()
   192  
   193  			if len(t.Values()) > 0 {
   194  				ps := t.Percentiles([]float64{50, 95, 99})
   195  				val := t.Values()
   196  
   197  				measurement := fmt.Sprintf("%s%s.span", namespace, name)
   198  				fields := map[string]interface{}{
   199  					"count": len(val),
   200  					"max":   val[len(val)-1],
   201  					"mean":  t.Mean(),
   202  					"min":   val[0],
   203  					"p50":   ps[0],
   204  					"p95":   ps[1],
   205  					"p99":   ps[2],
   206  				}
   207  
   208  				pt := influxdb2.NewPoint(measurement, r.tags, fields, now)
   209  				r.write.WritePoint(pt)
   210  			}
   211  		}
   212  	})
   213  
   214  	// Force all unwritten data to be sent
   215  	r.write.Flush()
   216  }