gitlab.com/aquachain/aquachain@v1.17.16-rc3.0.20221018032414-e3ddf1e1c055/common/metrics/librato/librato.go (about)

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