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

     1  package librato
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"math"
     7  	"regexp"
     8  	"time"
     9  
    10  	"github.com/intfoundation/intchain/metrics"
    11  )
    12  
    13  // a regexp for extracting the unit from time.Duration.String
    14  var unitRegexp = regexp.MustCompile(`[^\\d]+$`)
    15  
    16  // a helper that turns a time.Duration into librato display attributes for timer metrics
    17  func translateTimerAttributes(d time.Duration) (attrs map[string]interface{}) {
    18  	attrs = make(map[string]interface{})
    19  	attrs[DisplayTransform] = fmt.Sprintf("x/%d", int64(d))
    20  	attrs[DisplayUnitsShort] = string(unitRegexp.Find([]byte(d.String())))
    21  	return
    22  }
    23  
    24  type Reporter struct {
    25  	Email, Token    string
    26  	Namespace       string
    27  	Source          string
    28  	Interval        time.Duration
    29  	Registry        metrics.Registry
    30  	Percentiles     []float64              // percentiles to report on histogram metrics
    31  	TimerAttributes map[string]interface{} // units in which timers will be displayed
    32  	intervalSec     int64
    33  }
    34  
    35  func NewReporter(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) *Reporter {
    36  	return &Reporter{e, t, "", s, d, r, p, translateTimerAttributes(u), int64(d / time.Second)}
    37  }
    38  
    39  func Librato(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) {
    40  	NewReporter(r, d, e, t, s, p, u).Run()
    41  }
    42  
    43  func (self *Reporter) Run() {
    44  	log.Printf("WARNING: This client has been DEPRECATED! It has been moved to https://github.com/mihasya/go-metrics-librato and will be removed from rcrowley/go-metrics on August 5th 2015")
    45  	ticker := time.Tick(self.Interval)
    46  	metricsApi := &LibratoClient{self.Email, self.Token}
    47  	for now := range ticker {
    48  		var metrics Batch
    49  		var err error
    50  		if metrics, err = self.BuildRequest(now, self.Registry); err != nil {
    51  			log.Printf("ERROR constructing librato request body %s", err)
    52  			continue
    53  		}
    54  		if err := metricsApi.PostMetrics(metrics); err != nil {
    55  			log.Printf("ERROR sending metrics to librato %s", err)
    56  			continue
    57  		}
    58  	}
    59  }
    60  
    61  // calculate sum of squares from data provided by metrics.Histogram
    62  // see http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
    63  func sumSquares(s metrics.Sample) float64 {
    64  	count := float64(s.Count())
    65  	sumSquared := math.Pow(count*s.Mean(), 2)
    66  	sumSquares := math.Pow(count*s.StdDev(), 2) + sumSquared/count
    67  	if math.IsNaN(sumSquares) {
    68  		return 0.0
    69  	}
    70  	return sumSquares
    71  }
    72  func sumSquaresTimer(t metrics.Timer) float64 {
    73  	count := float64(t.Count())
    74  	sumSquared := math.Pow(count*t.Mean(), 2)
    75  	sumSquares := math.Pow(count*t.StdDev(), 2) + sumSquared/count
    76  	if math.IsNaN(sumSquares) {
    77  		return 0.0
    78  	}
    79  	return sumSquares
    80  }
    81  
    82  func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Batch, err error) {
    83  	snapshot = Batch{
    84  		// coerce timestamps to a stepping fn so that they line up in Librato graphs
    85  		MeasureTime: (now.Unix() / self.intervalSec) * self.intervalSec,
    86  		Source:      self.Source,
    87  	}
    88  	snapshot.Gauges = make([]Measurement, 0)
    89  	snapshot.Counters = make([]Measurement, 0)
    90  	histogramGaugeCount := 1 + len(self.Percentiles)
    91  	r.Each(func(name string, metric interface{}) {
    92  		if self.Namespace != "" {
    93  			name = fmt.Sprintf("%s.%s", self.Namespace, name)
    94  		}
    95  		measurement := Measurement{}
    96  		measurement[Period] = self.Interval.Seconds()
    97  		switch m := metric.(type) {
    98  		case metrics.Counter:
    99  			if m.Count() > 0 {
   100  				measurement[Name] = fmt.Sprintf("%s.%s", name, "count")
   101  				measurement[Value] = float64(m.Count())
   102  				measurement[Attributes] = map[string]interface{}{
   103  					DisplayUnitsLong:  Operations,
   104  					DisplayUnitsShort: OperationsShort,
   105  					DisplayMin:        "0",
   106  				}
   107  				snapshot.Counters = append(snapshot.Counters, measurement)
   108  			}
   109  		case metrics.Gauge:
   110  			measurement[Name] = name
   111  			measurement[Value] = float64(m.Value())
   112  			snapshot.Gauges = append(snapshot.Gauges, measurement)
   113  		case metrics.GaugeFloat64:
   114  			measurement[Name] = name
   115  			measurement[Value] = m.Value()
   116  			snapshot.Gauges = append(snapshot.Gauges, measurement)
   117  		case metrics.Histogram:
   118  			if m.Count() > 0 {
   119  				gauges := make([]Measurement, histogramGaugeCount)
   120  				s := m.Sample()
   121  				measurement[Name] = fmt.Sprintf("%s.%s", name, "hist")
   122  				measurement[Count] = uint64(s.Count())
   123  				measurement[Max] = float64(s.Max())
   124  				measurement[Min] = float64(s.Min())
   125  				measurement[Sum] = float64(s.Sum())
   126  				measurement[SumSquares] = sumSquares(s)
   127  				gauges[0] = measurement
   128  				for i, p := range self.Percentiles {
   129  					gauges[i+1] = Measurement{
   130  						Name:   fmt.Sprintf("%s.%.2f", measurement[Name], p),
   131  						Value:  s.Percentile(p),
   132  						Period: measurement[Period],
   133  					}
   134  				}
   135  				snapshot.Gauges = append(snapshot.Gauges, gauges...)
   136  			}
   137  		case metrics.Meter:
   138  			measurement[Name] = name
   139  			measurement[Value] = float64(m.Count())
   140  			snapshot.Counters = append(snapshot.Counters, measurement)
   141  			snapshot.Gauges = append(snapshot.Gauges,
   142  				Measurement{
   143  					Name:   fmt.Sprintf("%s.%s", name, "1min"),
   144  					Value:  m.Rate1(),
   145  					Period: int64(self.Interval.Seconds()),
   146  					Attributes: map[string]interface{}{
   147  						DisplayUnitsLong:  Operations,
   148  						DisplayUnitsShort: OperationsShort,
   149  						DisplayMin:        "0",
   150  					},
   151  				},
   152  				Measurement{
   153  					Name:   fmt.Sprintf("%s.%s", name, "5min"),
   154  					Value:  m.Rate5(),
   155  					Period: int64(self.Interval.Seconds()),
   156  					Attributes: map[string]interface{}{
   157  						DisplayUnitsLong:  Operations,
   158  						DisplayUnitsShort: OperationsShort,
   159  						DisplayMin:        "0",
   160  					},
   161  				},
   162  				Measurement{
   163  					Name:   fmt.Sprintf("%s.%s", name, "15min"),
   164  					Value:  m.Rate15(),
   165  					Period: int64(self.Interval.Seconds()),
   166  					Attributes: map[string]interface{}{
   167  						DisplayUnitsLong:  Operations,
   168  						DisplayUnitsShort: OperationsShort,
   169  						DisplayMin:        "0",
   170  					},
   171  				},
   172  			)
   173  		case metrics.Timer:
   174  			measurement[Name] = name
   175  			measurement[Value] = float64(m.Count())
   176  			snapshot.Counters = append(snapshot.Counters, measurement)
   177  			if m.Count() > 0 {
   178  				libratoName := fmt.Sprintf("%s.%s", name, "timer.mean")
   179  				gauges := make([]Measurement, histogramGaugeCount)
   180  				gauges[0] = Measurement{
   181  					Name:       libratoName,
   182  					Count:      uint64(m.Count()),
   183  					Sum:        m.Mean() * float64(m.Count()),
   184  					Max:        float64(m.Max()),
   185  					Min:        float64(m.Min()),
   186  					SumSquares: sumSquaresTimer(m),
   187  					Period:     int64(self.Interval.Seconds()),
   188  					Attributes: self.TimerAttributes,
   189  				}
   190  				for i, p := range self.Percentiles {
   191  					gauges[i+1] = Measurement{
   192  						Name:       fmt.Sprintf("%s.timer.%2.0f", name, p*100),
   193  						Value:      m.Percentile(p),
   194  						Period:     int64(self.Interval.Seconds()),
   195  						Attributes: self.TimerAttributes,
   196  					}
   197  				}
   198  				snapshot.Gauges = append(snapshot.Gauges, gauges...)
   199  				snapshot.Gauges = append(snapshot.Gauges,
   200  					Measurement{
   201  						Name:   fmt.Sprintf("%s.%s", name, "rate.1min"),
   202  						Value:  m.Rate1(),
   203  						Period: int64(self.Interval.Seconds()),
   204  						Attributes: map[string]interface{}{
   205  							DisplayUnitsLong:  Operations,
   206  							DisplayUnitsShort: OperationsShort,
   207  							DisplayMin:        "0",
   208  						},
   209  					},
   210  					Measurement{
   211  						Name:   fmt.Sprintf("%s.%s", name, "rate.5min"),
   212  						Value:  m.Rate5(),
   213  						Period: int64(self.Interval.Seconds()),
   214  						Attributes: map[string]interface{}{
   215  							DisplayUnitsLong:  Operations,
   216  							DisplayUnitsShort: OperationsShort,
   217  							DisplayMin:        "0",
   218  						},
   219  					},
   220  					Measurement{
   221  						Name:   fmt.Sprintf("%s.%s", name, "rate.15min"),
   222  						Value:  m.Rate15(),
   223  						Period: int64(self.Interval.Seconds()),
   224  						Attributes: map[string]interface{}{
   225  							DisplayUnitsLong:  Operations,
   226  							DisplayUnitsShort: OperationsShort,
   227  							DisplayMin:        "0",
   228  						},
   229  					},
   230  				)
   231  			}
   232  		}
   233  	})
   234  	return
   235  }