github.com/DxChainNetwork/dxc@v0.8.1-0.20220824085222-1162e304b6e7/metrics/influxdb/influxdb.go (about)

     1  package influxdb
     2  
     3  import (
     4  	"fmt"
     5  	uurl "net/url"
     6  	"time"
     7  
     8  	"github.com/DxChainNetwork/dxc/log"
     9  	"github.com/DxChainNetwork/dxc/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  
   166  			if ms.Count() > 0 {
   167  				ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
   168  				pts = append(pts, client.Point{
   169  					Measurement: fmt.Sprintf("%s%s.histogram", namespace, name),
   170  					Tags:        r.tags,
   171  					Fields: map[string]interface{}{
   172  						"count":    ms.Count(),
   173  						"max":      ms.Max(),
   174  						"mean":     ms.Mean(),
   175  						"min":      ms.Min(),
   176  						"stddev":   ms.StdDev(),
   177  						"variance": ms.Variance(),
   178  						"p50":      ps[0],
   179  						"p75":      ps[1],
   180  						"p95":      ps[2],
   181  						"p99":      ps[3],
   182  						"p999":     ps[4],
   183  						"p9999":    ps[5],
   184  					},
   185  					Time: now,
   186  				})
   187  			}
   188  		case metrics.Meter:
   189  			ms := metric.Snapshot()
   190  			pts = append(pts, client.Point{
   191  				Measurement: fmt.Sprintf("%s%s.meter", namespace, name),
   192  				Tags:        r.tags,
   193  				Fields: map[string]interface{}{
   194  					"count": ms.Count(),
   195  					"m1":    ms.Rate1(),
   196  					"m5":    ms.Rate5(),
   197  					"m15":   ms.Rate15(),
   198  					"mean":  ms.RateMean(),
   199  				},
   200  				Time: now,
   201  			})
   202  		case metrics.Timer:
   203  			ms := metric.Snapshot()
   204  			ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
   205  			pts = append(pts, client.Point{
   206  				Measurement: fmt.Sprintf("%s%s.timer", namespace, name),
   207  				Tags:        r.tags,
   208  				Fields: map[string]interface{}{
   209  					"count":    ms.Count(),
   210  					"max":      ms.Max(),
   211  					"mean":     ms.Mean(),
   212  					"min":      ms.Min(),
   213  					"stddev":   ms.StdDev(),
   214  					"variance": ms.Variance(),
   215  					"p50":      ps[0],
   216  					"p75":      ps[1],
   217  					"p95":      ps[2],
   218  					"p99":      ps[3],
   219  					"p999":     ps[4],
   220  					"p9999":    ps[5],
   221  					"m1":       ms.Rate1(),
   222  					"m5":       ms.Rate5(),
   223  					"m15":      ms.Rate15(),
   224  					"meanrate": ms.RateMean(),
   225  				},
   226  				Time: now,
   227  			})
   228  		case metrics.ResettingTimer:
   229  			t := metric.Snapshot()
   230  
   231  			if len(t.Values()) > 0 {
   232  				ps := t.Percentiles([]float64{50, 95, 99})
   233  				val := t.Values()
   234  				pts = append(pts, client.Point{
   235  					Measurement: fmt.Sprintf("%s%s.span", namespace, name),
   236  					Tags:        r.tags,
   237  					Fields: map[string]interface{}{
   238  						"count": len(val),
   239  						"max":   val[len(val)-1],
   240  						"mean":  t.Mean(),
   241  						"min":   val[0],
   242  						"p50":   ps[0],
   243  						"p95":   ps[1],
   244  						"p99":   ps[2],
   245  					},
   246  					Time: now,
   247  				})
   248  			}
   249  		}
   250  	})
   251  
   252  	bps := client.BatchPoints{
   253  		Points:   pts,
   254  		Database: r.database,
   255  	}
   256  
   257  	_, err := r.client.Write(bps)
   258  	return err
   259  }