github.com/intfoundation/intchain@v0.0.0-20220727031208-4316ad31ca73/metrics/influxdb/influxdb.go (about)

     1  package influxdb
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	uurl "net/url"
     7  	"time"
     8  
     9  	"github.com/influxdata/influxdb/client"
    10  	"github.com/intfoundation/intchain/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.Printf("unable to parse InfluxDB url %s. err=%v", url, 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.Printf("unable to make InfluxDB client. err=%v", err)
    55  		return
    56  	}
    57  
    58  	rep.run()
    59  }
    60  
    61  func (r *reporter) makeClient() (err error) {
    62  	r.client, err = client.NewClient(client.Config{
    63  		URL:      r.url,
    64  		Username: r.username,
    65  		Password: r.password,
    66  	})
    67  
    68  	return
    69  }
    70  
    71  func (r *reporter) run() {
    72  	intervalTicker := time.Tick(r.interval)
    73  	pingTicker := time.Tick(time.Second * 5)
    74  
    75  	for {
    76  		select {
    77  		case <-intervalTicker:
    78  			if err := r.send(); err != nil {
    79  				log.Printf("unable to send to InfluxDB. err=%v", err)
    80  			}
    81  		case <-pingTicker:
    82  			_, _, err := r.client.Ping()
    83  			if err != nil {
    84  				log.Printf("got error while sending a ping to InfluxDB, trying to recreate client. err=%v", err)
    85  
    86  				if err = r.makeClient(); err != nil {
    87  					log.Printf("unable to make InfluxDB client. err=%v", err)
    88  				}
    89  			}
    90  		}
    91  	}
    92  }
    93  
    94  func (r *reporter) send() error {
    95  	var pts []client.Point
    96  
    97  	r.reg.Each(func(name string, i interface{}) {
    98  		now := time.Now()
    99  		namespace := r.namespace
   100  
   101  		switch metric := i.(type) {
   102  		case metrics.Counter:
   103  			v := metric.Count()
   104  			l := r.cache[name]
   105  			pts = append(pts, client.Point{
   106  				Measurement: fmt.Sprintf("%s%s.count", namespace, name),
   107  				Tags:        r.tags,
   108  				Fields: map[string]interface{}{
   109  					"value": v - l,
   110  				},
   111  				Time: now,
   112  			})
   113  			r.cache[name] = v
   114  		case metrics.Gauge:
   115  			ms := metric.Snapshot()
   116  			pts = append(pts, client.Point{
   117  				Measurement: fmt.Sprintf("%s%s.gauge", namespace, name),
   118  				Tags:        r.tags,
   119  				Fields: map[string]interface{}{
   120  					"value": ms.Value(),
   121  				},
   122  				Time: now,
   123  			})
   124  		case metrics.GaugeFloat64:
   125  			ms := metric.Snapshot()
   126  			pts = append(pts, client.Point{
   127  				Measurement: fmt.Sprintf("%s%s.gauge", namespace, name),
   128  				Tags:        r.tags,
   129  				Fields: map[string]interface{}{
   130  					"value": ms.Value(),
   131  				},
   132  				Time: now,
   133  			})
   134  		case metrics.Histogram:
   135  			ms := metric.Snapshot()
   136  			ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
   137  			pts = append(pts, client.Point{
   138  				Measurement: fmt.Sprintf("%s%s.histogram", namespace, name),
   139  				Tags:        r.tags,
   140  				Fields: map[string]interface{}{
   141  					"count":    ms.Count(),
   142  					"max":      ms.Max(),
   143  					"mean":     ms.Mean(),
   144  					"min":      ms.Min(),
   145  					"stddev":   ms.StdDev(),
   146  					"variance": ms.Variance(),
   147  					"p50":      ps[0],
   148  					"p75":      ps[1],
   149  					"p95":      ps[2],
   150  					"p99":      ps[3],
   151  					"p999":     ps[4],
   152  					"p9999":    ps[5],
   153  				},
   154  				Time: now,
   155  			})
   156  		case metrics.Meter:
   157  			ms := metric.Snapshot()
   158  			pts = append(pts, client.Point{
   159  				Measurement: fmt.Sprintf("%s%s.meter", namespace, name),
   160  				Tags:        r.tags,
   161  				Fields: map[string]interface{}{
   162  					"count": ms.Count(),
   163  					"m1":    ms.Rate1(),
   164  					"m5":    ms.Rate5(),
   165  					"m15":   ms.Rate15(),
   166  					"mean":  ms.RateMean(),
   167  				},
   168  				Time: now,
   169  			})
   170  		case metrics.Timer:
   171  			ms := metric.Snapshot()
   172  			ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
   173  			pts = append(pts, client.Point{
   174  				Measurement: fmt.Sprintf("%s%s.timer", namespace, name),
   175  				Tags:        r.tags,
   176  				Fields: map[string]interface{}{
   177  					"count":    ms.Count(),
   178  					"max":      ms.Max(),
   179  					"mean":     ms.Mean(),
   180  					"min":      ms.Min(),
   181  					"stddev":   ms.StdDev(),
   182  					"variance": ms.Variance(),
   183  					"p50":      ps[0],
   184  					"p75":      ps[1],
   185  					"p95":      ps[2],
   186  					"p99":      ps[3],
   187  					"p999":     ps[4],
   188  					"p9999":    ps[5],
   189  					"m1":       ms.Rate1(),
   190  					"m5":       ms.Rate5(),
   191  					"m15":      ms.Rate15(),
   192  					"meanrate": ms.RateMean(),
   193  				},
   194  				Time: now,
   195  			})
   196  		case metrics.ResettingTimer:
   197  			t := metric.Snapshot()
   198  
   199  			if len(t.Values()) > 0 {
   200  				ps := t.Percentiles([]float64{50, 95, 99})
   201  				val := t.Values()
   202  				pts = append(pts, client.Point{
   203  					Measurement: fmt.Sprintf("%s%s.span", namespace, name),
   204  					Tags:        r.tags,
   205  					Fields: map[string]interface{}{
   206  						"count": len(val),
   207  						"max":   val[len(val)-1],
   208  						"mean":  t.Mean(),
   209  						"min":   val[0],
   210  						"p50":   ps[0],
   211  						"p95":   ps[1],
   212  						"p99":   ps[2],
   213  					},
   214  					Time: now,
   215  				})
   216  			}
   217  		}
   218  	})
   219  
   220  	bps := client.BatchPoints{
   221  		Points:   pts,
   222  		Database: r.database,
   223  	}
   224  
   225  	_, err := r.client.Write(bps)
   226  	return err
   227  }