github.com/turingchain2020/turingchain@v1.1.21/metrics/influxdb/influxdb.go (about)

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