github.com/theQRL/go-zond@v0.1.1/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/theQRL/go-zond/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 (rep *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.NewTicker(rep.Interval)
    46  	defer ticker.Stop()
    47  	metricsApi := &LibratoClient{rep.Email, rep.Token}
    48  	for now := range ticker.C {
    49  		var metrics Batch
    50  		var err error
    51  		if metrics, err = rep.BuildRequest(now, rep.Registry); err != nil {
    52  			log.Printf("ERROR constructing librato request body %s", err)
    53  			continue
    54  		}
    55  		if err := metricsApi.PostMetrics(metrics); err != nil {
    56  			log.Printf("ERROR sending metrics to librato %s", err)
    57  			continue
    58  		}
    59  	}
    60  }
    61  
    62  // calculate sum of squares from data provided by metrics.Histogram
    63  // see http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
    64  func sumSquares(icount int64, mean, stDev float64) float64 {
    65  	count := float64(icount)
    66  	sumSquared := math.Pow(count*mean, 2)
    67  	sumSquares := math.Pow(count*stDev, 2) + sumSquared/count
    68  	if math.IsNaN(sumSquares) {
    69  		return 0.0
    70  	}
    71  	return sumSquares
    72  }
    73  func sumSquaresTimer(t metrics.TimerSnapshot) float64 {
    74  	count := float64(t.Count())
    75  	sumSquared := math.Pow(count*t.Mean(), 2)
    76  	sumSquares := math.Pow(count*t.StdDev(), 2) + sumSquared/count
    77  	if math.IsNaN(sumSquares) {
    78  		return 0.0
    79  	}
    80  	return sumSquares
    81  }
    82  
    83  func (rep *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Batch, err error) {
    84  	snapshot = Batch{
    85  		// coerce timestamps to a stepping fn so that they line up in Librato graphs
    86  		MeasureTime: (now.Unix() / rep.intervalSec) * rep.intervalSec,
    87  		Source:      rep.Source,
    88  	}
    89  	snapshot.Gauges = make([]Measurement, 0)
    90  	snapshot.Counters = make([]Measurement, 0)
    91  	histogramGaugeCount := 1 + len(rep.Percentiles)
    92  	r.Each(func(name string, metric interface{}) {
    93  		if rep.Namespace != "" {
    94  			name = fmt.Sprintf("%s.%s", rep.Namespace, name)
    95  		}
    96  		measurement := Measurement{}
    97  		measurement[Period] = rep.Interval.Seconds()
    98  		switch m := metric.(type) {
    99  		case metrics.Counter:
   100  			ms := m.Snapshot()
   101  			if ms.Count() > 0 {
   102  				measurement[Name] = fmt.Sprintf("%s.%s", name, "count")
   103  				measurement[Value] = float64(ms.Count())
   104  				measurement[Attributes] = map[string]interface{}{
   105  					DisplayUnitsLong:  Operations,
   106  					DisplayUnitsShort: OperationsShort,
   107  					DisplayMin:        "0",
   108  				}
   109  				snapshot.Counters = append(snapshot.Counters, measurement)
   110  			}
   111  		case metrics.CounterFloat64:
   112  			if count := m.Snapshot().Count(); count > 0 {
   113  				measurement[Name] = fmt.Sprintf("%s.%s", name, "count")
   114  				measurement[Value] = count
   115  				measurement[Attributes] = map[string]interface{}{
   116  					DisplayUnitsLong:  Operations,
   117  					DisplayUnitsShort: OperationsShort,
   118  					DisplayMin:        "0",
   119  				}
   120  				snapshot.Counters = append(snapshot.Counters, measurement)
   121  			}
   122  		case metrics.Gauge:
   123  			measurement[Name] = name
   124  			measurement[Value] = float64(m.Snapshot().Value())
   125  			snapshot.Gauges = append(snapshot.Gauges, measurement)
   126  		case metrics.GaugeFloat64:
   127  			measurement[Name] = name
   128  			measurement[Value] = m.Snapshot().Value()
   129  			snapshot.Gauges = append(snapshot.Gauges, measurement)
   130  		case metrics.GaugeInfo:
   131  			measurement[Name] = name
   132  			measurement[Value] = m.Snapshot().Value()
   133  			snapshot.Gauges = append(snapshot.Gauges, measurement)
   134  		case metrics.Histogram:
   135  			ms := m.Snapshot()
   136  			if ms.Count() > 0 {
   137  				gauges := make([]Measurement, histogramGaugeCount)
   138  				measurement[Name] = fmt.Sprintf("%s.%s", name, "hist")
   139  				measurement[Count] = uint64(ms.Count())
   140  				measurement[Max] = float64(ms.Max())
   141  				measurement[Min] = float64(ms.Min())
   142  				measurement[Sum] = float64(ms.Sum())
   143  				measurement[SumSquares] = sumSquares(ms.Count(), ms.Mean(), ms.StdDev())
   144  				gauges[0] = measurement
   145  				for i, p := range rep.Percentiles {
   146  					gauges[i+1] = Measurement{
   147  						Name:   fmt.Sprintf("%s.%.2f", measurement[Name], p),
   148  						Value:  ms.Percentile(p),
   149  						Period: measurement[Period],
   150  					}
   151  				}
   152  				snapshot.Gauges = append(snapshot.Gauges, gauges...)
   153  			}
   154  		case metrics.Meter:
   155  			ms := m.Snapshot()
   156  			measurement[Name] = name
   157  			measurement[Value] = float64(ms.Count())
   158  			snapshot.Counters = append(snapshot.Counters, measurement)
   159  			snapshot.Gauges = append(snapshot.Gauges,
   160  				Measurement{
   161  					Name:   fmt.Sprintf("%s.%s", name, "1min"),
   162  					Value:  ms.Rate1(),
   163  					Period: int64(rep.Interval.Seconds()),
   164  					Attributes: map[string]interface{}{
   165  						DisplayUnitsLong:  Operations,
   166  						DisplayUnitsShort: OperationsShort,
   167  						DisplayMin:        "0",
   168  					},
   169  				},
   170  				Measurement{
   171  					Name:   fmt.Sprintf("%s.%s", name, "5min"),
   172  					Value:  ms.Rate5(),
   173  					Period: int64(rep.Interval.Seconds()),
   174  					Attributes: map[string]interface{}{
   175  						DisplayUnitsLong:  Operations,
   176  						DisplayUnitsShort: OperationsShort,
   177  						DisplayMin:        "0",
   178  					},
   179  				},
   180  				Measurement{
   181  					Name:   fmt.Sprintf("%s.%s", name, "15min"),
   182  					Value:  ms.Rate15(),
   183  					Period: int64(rep.Interval.Seconds()),
   184  					Attributes: map[string]interface{}{
   185  						DisplayUnitsLong:  Operations,
   186  						DisplayUnitsShort: OperationsShort,
   187  						DisplayMin:        "0",
   188  					},
   189  				},
   190  			)
   191  		case metrics.Timer:
   192  			ms := m.Snapshot()
   193  			measurement[Name] = name
   194  			measurement[Value] = float64(ms.Count())
   195  			snapshot.Counters = append(snapshot.Counters, measurement)
   196  			if ms.Count() > 0 {
   197  				libratoName := fmt.Sprintf("%s.%s", name, "timer.mean")
   198  				gauges := make([]Measurement, histogramGaugeCount)
   199  				gauges[0] = Measurement{
   200  					Name:       libratoName,
   201  					Count:      uint64(ms.Count()),
   202  					Sum:        ms.Mean() * float64(ms.Count()),
   203  					Max:        float64(ms.Max()),
   204  					Min:        float64(ms.Min()),
   205  					SumSquares: sumSquaresTimer(ms),
   206  					Period:     int64(rep.Interval.Seconds()),
   207  					Attributes: rep.TimerAttributes,
   208  				}
   209  				for i, p := range rep.Percentiles {
   210  					gauges[i+1] = Measurement{
   211  						Name:       fmt.Sprintf("%s.timer.%2.0f", name, p*100),
   212  						Value:      ms.Percentile(p),
   213  						Period:     int64(rep.Interval.Seconds()),
   214  						Attributes: rep.TimerAttributes,
   215  					}
   216  				}
   217  				snapshot.Gauges = append(snapshot.Gauges, gauges...)
   218  				snapshot.Gauges = append(snapshot.Gauges,
   219  					Measurement{
   220  						Name:   fmt.Sprintf("%s.%s", name, "rate.1min"),
   221  						Value:  ms.Rate1(),
   222  						Period: int64(rep.Interval.Seconds()),
   223  						Attributes: map[string]interface{}{
   224  							DisplayUnitsLong:  Operations,
   225  							DisplayUnitsShort: OperationsShort,
   226  							DisplayMin:        "0",
   227  						},
   228  					},
   229  					Measurement{
   230  						Name:   fmt.Sprintf("%s.%s", name, "rate.5min"),
   231  						Value:  ms.Rate5(),
   232  						Period: int64(rep.Interval.Seconds()),
   233  						Attributes: map[string]interface{}{
   234  							DisplayUnitsLong:  Operations,
   235  							DisplayUnitsShort: OperationsShort,
   236  							DisplayMin:        "0",
   237  						},
   238  					},
   239  					Measurement{
   240  						Name:   fmt.Sprintf("%s.%s", name, "rate.15min"),
   241  						Value:  ms.Rate15(),
   242  						Period: int64(rep.Interval.Seconds()),
   243  						Attributes: map[string]interface{}{
   244  							DisplayUnitsLong:  Operations,
   245  							DisplayUnitsShort: OperationsShort,
   246  							DisplayMin:        "0",
   247  						},
   248  					},
   249  				)
   250  			}
   251  		}
   252  	})
   253  	return
   254  }