236 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			236 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								package librato
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"fmt"
							 | 
						||
| 
								 | 
							
									"log"
							 | 
						||
| 
								 | 
							
									"math"
							 | 
						||
| 
								 | 
							
									"regexp"
							 | 
						||
| 
								 | 
							
									"time"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									"github.com/ethereum/go-ethereum/metrics"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// a regexp for extracting the unit from time.Duration.String
							 | 
						||
| 
								 | 
							
								var unitRegexp = regexp.MustCompile(`[^\\d]+$`)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// a helper that turns a time.Duration into librato display attributes for timer metrics
							 | 
						||
| 
								 | 
							
								func translateTimerAttributes(d time.Duration) (attrs map[string]interface{}) {
							 | 
						||
| 
								 | 
							
									attrs = make(map[string]interface{})
							 | 
						||
| 
								 | 
							
									attrs[DisplayTransform] = fmt.Sprintf("x/%d", int64(d))
							 | 
						||
| 
								 | 
							
									attrs[DisplayUnitsShort] = string(unitRegexp.Find([]byte(d.String())))
							 | 
						||
| 
								 | 
							
									return
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type Reporter struct {
							 | 
						||
| 
								 | 
							
									Email, Token    string
							 | 
						||
| 
								 | 
							
									Namespace       string
							 | 
						||
| 
								 | 
							
									Source          string
							 | 
						||
| 
								 | 
							
									Interval        time.Duration
							 | 
						||
| 
								 | 
							
									Registry        metrics.Registry
							 | 
						||
| 
								 | 
							
									Percentiles     []float64              // percentiles to report on histogram metrics
							 | 
						||
| 
								 | 
							
									TimerAttributes map[string]interface{} // units in which timers will be displayed
							 | 
						||
| 
								 | 
							
									intervalSec     int64
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func NewReporter(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) *Reporter {
							 | 
						||
| 
								 | 
							
									return &Reporter{e, t, "", s, d, r, p, translateTimerAttributes(u), int64(d / time.Second)}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func Librato(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) {
							 | 
						||
| 
								 | 
							
									NewReporter(r, d, e, t, s, p, u).Run()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (self *Reporter) Run() {
							 | 
						||
| 
								 | 
							
									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")
							 | 
						||
| 
								 | 
							
									ticker := time.Tick(self.Interval)
							 | 
						||
| 
								 | 
							
									metricsApi := &LibratoClient{self.Email, self.Token}
							 | 
						||
| 
								 | 
							
									for now := range ticker {
							 | 
						||
| 
								 | 
							
										var metrics Batch
							 | 
						||
| 
								 | 
							
										var err error
							 | 
						||
| 
								 | 
							
										if metrics, err = self.BuildRequest(now, self.Registry); err != nil {
							 | 
						||
| 
								 | 
							
											log.Printf("ERROR constructing librato request body %s", err)
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if err := metricsApi.PostMetrics(metrics); err != nil {
							 | 
						||
| 
								 | 
							
											log.Printf("ERROR sending metrics to librato %s", err)
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// calculate sum of squares from data provided by metrics.Histogram
							 | 
						||
| 
								 | 
							
								// see http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
							 | 
						||
| 
								 | 
							
								func sumSquares(s metrics.Sample) float64 {
							 | 
						||
| 
								 | 
							
									count := float64(s.Count())
							 | 
						||
| 
								 | 
							
									sumSquared := math.Pow(count*s.Mean(), 2)
							 | 
						||
| 
								 | 
							
									sumSquares := math.Pow(count*s.StdDev(), 2) + sumSquared/count
							 | 
						||
| 
								 | 
							
									if math.IsNaN(sumSquares) {
							 | 
						||
| 
								 | 
							
										return 0.0
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return sumSquares
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								func sumSquaresTimer(t metrics.Timer) float64 {
							 | 
						||
| 
								 | 
							
									count := float64(t.Count())
							 | 
						||
| 
								 | 
							
									sumSquared := math.Pow(count*t.Mean(), 2)
							 | 
						||
| 
								 | 
							
									sumSquares := math.Pow(count*t.StdDev(), 2) + sumSquared/count
							 | 
						||
| 
								 | 
							
									if math.IsNaN(sumSquares) {
							 | 
						||
| 
								 | 
							
										return 0.0
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return sumSquares
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Batch, err error) {
							 | 
						||
| 
								 | 
							
									snapshot = Batch{
							 | 
						||
| 
								 | 
							
										// coerce timestamps to a stepping fn so that they line up in Librato graphs
							 | 
						||
| 
								 | 
							
										MeasureTime: (now.Unix() / self.intervalSec) * self.intervalSec,
							 | 
						||
| 
								 | 
							
										Source:      self.Source,
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									snapshot.Gauges = make([]Measurement, 0)
							 | 
						||
| 
								 | 
							
									snapshot.Counters = make([]Measurement, 0)
							 | 
						||
| 
								 | 
							
									histogramGaugeCount := 1 + len(self.Percentiles)
							 | 
						||
| 
								 | 
							
									r.Each(func(name string, metric interface{}) {
							 | 
						||
| 
								 | 
							
										if self.Namespace != "" {
							 | 
						||
| 
								 | 
							
											name = fmt.Sprintf("%s.%s", self.Namespace, name)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										measurement := Measurement{}
							 | 
						||
| 
								 | 
							
										measurement[Period] = self.Interval.Seconds()
							 | 
						||
| 
								 | 
							
										switch m := metric.(type) {
							 | 
						||
| 
								 | 
							
										case metrics.Counter:
							 | 
						||
| 
								 | 
							
											if m.Count() > 0 {
							 | 
						||
| 
								 | 
							
												measurement[Name] = fmt.Sprintf("%s.%s", name, "count")
							 | 
						||
| 
								 | 
							
												measurement[Value] = float64(m.Count())
							 | 
						||
| 
								 | 
							
												measurement[Attributes] = map[string]interface{}{
							 | 
						||
| 
								 | 
							
													DisplayUnitsLong:  Operations,
							 | 
						||
| 
								 | 
							
													DisplayUnitsShort: OperationsShort,
							 | 
						||
| 
								 | 
							
													DisplayMin:        "0",
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												snapshot.Counters = append(snapshot.Counters, measurement)
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										case metrics.Gauge:
							 | 
						||
| 
								 | 
							
											measurement[Name] = name
							 | 
						||
| 
								 | 
							
											measurement[Value] = float64(m.Value())
							 | 
						||
| 
								 | 
							
											snapshot.Gauges = append(snapshot.Gauges, measurement)
							 | 
						||
| 
								 | 
							
										case metrics.GaugeFloat64:
							 | 
						||
| 
								 | 
							
											measurement[Name] = name
							 | 
						||
| 
								 | 
							
											measurement[Value] = m.Value()
							 | 
						||
| 
								 | 
							
											snapshot.Gauges = append(snapshot.Gauges, measurement)
							 | 
						||
| 
								 | 
							
										case metrics.Histogram:
							 | 
						||
| 
								 | 
							
											if m.Count() > 0 {
							 | 
						||
| 
								 | 
							
												gauges := make([]Measurement, histogramGaugeCount)
							 | 
						||
| 
								 | 
							
												s := m.Sample()
							 | 
						||
| 
								 | 
							
												measurement[Name] = fmt.Sprintf("%s.%s", name, "hist")
							 | 
						||
| 
								 | 
							
												measurement[Count] = uint64(s.Count())
							 | 
						||
| 
								 | 
							
												measurement[Max] = float64(s.Max())
							 | 
						||
| 
								 | 
							
												measurement[Min] = float64(s.Min())
							 | 
						||
| 
								 | 
							
												measurement[Sum] = float64(s.Sum())
							 | 
						||
| 
								 | 
							
												measurement[SumSquares] = sumSquares(s)
							 | 
						||
| 
								 | 
							
												gauges[0] = measurement
							 | 
						||
| 
								 | 
							
												for i, p := range self.Percentiles {
							 | 
						||
| 
								 | 
							
													gauges[i+1] = Measurement{
							 | 
						||
| 
								 | 
							
														Name:   fmt.Sprintf("%s.%.2f", measurement[Name], p),
							 | 
						||
| 
								 | 
							
														Value:  s.Percentile(p),
							 | 
						||
| 
								 | 
							
														Period: measurement[Period],
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												snapshot.Gauges = append(snapshot.Gauges, gauges...)
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										case metrics.Meter:
							 | 
						||
| 
								 | 
							
											measurement[Name] = name
							 | 
						||
| 
								 | 
							
											measurement[Value] = float64(m.Count())
							 | 
						||
| 
								 | 
							
											snapshot.Counters = append(snapshot.Counters, measurement)
							 | 
						||
| 
								 | 
							
											snapshot.Gauges = append(snapshot.Gauges,
							 | 
						||
| 
								 | 
							
												Measurement{
							 | 
						||
| 
								 | 
							
													Name:   fmt.Sprintf("%s.%s", name, "1min"),
							 | 
						||
| 
								 | 
							
													Value:  m.Rate1(),
							 | 
						||
| 
								 | 
							
													Period: int64(self.Interval.Seconds()),
							 | 
						||
| 
								 | 
							
													Attributes: map[string]interface{}{
							 | 
						||
| 
								 | 
							
														DisplayUnitsLong:  Operations,
							 | 
						||
| 
								 | 
							
														DisplayUnitsShort: OperationsShort,
							 | 
						||
| 
								 | 
							
														DisplayMin:        "0",
							 | 
						||
| 
								 | 
							
													},
							 | 
						||
| 
								 | 
							
												},
							 | 
						||
| 
								 | 
							
												Measurement{
							 | 
						||
| 
								 | 
							
													Name:   fmt.Sprintf("%s.%s", name, "5min"),
							 | 
						||
| 
								 | 
							
													Value:  m.Rate5(),
							 | 
						||
| 
								 | 
							
													Period: int64(self.Interval.Seconds()),
							 | 
						||
| 
								 | 
							
													Attributes: map[string]interface{}{
							 | 
						||
| 
								 | 
							
														DisplayUnitsLong:  Operations,
							 | 
						||
| 
								 | 
							
														DisplayUnitsShort: OperationsShort,
							 | 
						||
| 
								 | 
							
														DisplayMin:        "0",
							 | 
						||
| 
								 | 
							
													},
							 | 
						||
| 
								 | 
							
												},
							 | 
						||
| 
								 | 
							
												Measurement{
							 | 
						||
| 
								 | 
							
													Name:   fmt.Sprintf("%s.%s", name, "15min"),
							 | 
						||
| 
								 | 
							
													Value:  m.Rate15(),
							 | 
						||
| 
								 | 
							
													Period: int64(self.Interval.Seconds()),
							 | 
						||
| 
								 | 
							
													Attributes: map[string]interface{}{
							 | 
						||
| 
								 | 
							
														DisplayUnitsLong:  Operations,
							 | 
						||
| 
								 | 
							
														DisplayUnitsShort: OperationsShort,
							 | 
						||
| 
								 | 
							
														DisplayMin:        "0",
							 | 
						||
| 
								 | 
							
													},
							 | 
						||
| 
								 | 
							
												},
							 | 
						||
| 
								 | 
							
											)
							 | 
						||
| 
								 | 
							
										case metrics.Timer:
							 | 
						||
| 
								 | 
							
											measurement[Name] = name
							 | 
						||
| 
								 | 
							
											measurement[Value] = float64(m.Count())
							 | 
						||
| 
								 | 
							
											snapshot.Counters = append(snapshot.Counters, measurement)
							 | 
						||
| 
								 | 
							
											if m.Count() > 0 {
							 | 
						||
| 
								 | 
							
												libratoName := fmt.Sprintf("%s.%s", name, "timer.mean")
							 | 
						||
| 
								 | 
							
												gauges := make([]Measurement, histogramGaugeCount)
							 | 
						||
| 
								 | 
							
												gauges[0] = Measurement{
							 | 
						||
| 
								 | 
							
													Name:       libratoName,
							 | 
						||
| 
								 | 
							
													Count:      uint64(m.Count()),
							 | 
						||
| 
								 | 
							
													Sum:        m.Mean() * float64(m.Count()),
							 | 
						||
| 
								 | 
							
													Max:        float64(m.Max()),
							 | 
						||
| 
								 | 
							
													Min:        float64(m.Min()),
							 | 
						||
| 
								 | 
							
													SumSquares: sumSquaresTimer(m),
							 | 
						||
| 
								 | 
							
													Period:     int64(self.Interval.Seconds()),
							 | 
						||
| 
								 | 
							
													Attributes: self.TimerAttributes,
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												for i, p := range self.Percentiles {
							 | 
						||
| 
								 | 
							
													gauges[i+1] = Measurement{
							 | 
						||
| 
								 | 
							
														Name:       fmt.Sprintf("%s.timer.%2.0f", name, p*100),
							 | 
						||
| 
								 | 
							
														Value:      m.Percentile(p),
							 | 
						||
| 
								 | 
							
														Period:     int64(self.Interval.Seconds()),
							 | 
						||
| 
								 | 
							
														Attributes: self.TimerAttributes,
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												snapshot.Gauges = append(snapshot.Gauges, gauges...)
							 | 
						||
| 
								 | 
							
												snapshot.Gauges = append(snapshot.Gauges,
							 | 
						||
| 
								 | 
							
													Measurement{
							 | 
						||
| 
								 | 
							
														Name:   fmt.Sprintf("%s.%s", name, "rate.1min"),
							 | 
						||
| 
								 | 
							
														Value:  m.Rate1(),
							 | 
						||
| 
								 | 
							
														Period: int64(self.Interval.Seconds()),
							 | 
						||
| 
								 | 
							
														Attributes: map[string]interface{}{
							 | 
						||
| 
								 | 
							
															DisplayUnitsLong:  Operations,
							 | 
						||
| 
								 | 
							
															DisplayUnitsShort: OperationsShort,
							 | 
						||
| 
								 | 
							
															DisplayMin:        "0",
							 | 
						||
| 
								 | 
							
														},
							 | 
						||
| 
								 | 
							
													},
							 | 
						||
| 
								 | 
							
													Measurement{
							 | 
						||
| 
								 | 
							
														Name:   fmt.Sprintf("%s.%s", name, "rate.5min"),
							 | 
						||
| 
								 | 
							
														Value:  m.Rate5(),
							 | 
						||
| 
								 | 
							
														Period: int64(self.Interval.Seconds()),
							 | 
						||
| 
								 | 
							
														Attributes: map[string]interface{}{
							 | 
						||
| 
								 | 
							
															DisplayUnitsLong:  Operations,
							 | 
						||
| 
								 | 
							
															DisplayUnitsShort: OperationsShort,
							 | 
						||
| 
								 | 
							
															DisplayMin:        "0",
							 | 
						||
| 
								 | 
							
														},
							 | 
						||
| 
								 | 
							
													},
							 | 
						||
| 
								 | 
							
													Measurement{
							 | 
						||
| 
								 | 
							
														Name:   fmt.Sprintf("%s.%s", name, "rate.15min"),
							 | 
						||
| 
								 | 
							
														Value:  m.Rate15(),
							 | 
						||
| 
								 | 
							
														Period: int64(self.Interval.Seconds()),
							 | 
						||
| 
								 | 
							
														Attributes: map[string]interface{}{
							 | 
						||
| 
								 | 
							
															DisplayUnitsLong:  Operations,
							 | 
						||
| 
								 | 
							
															DisplayUnitsShort: OperationsShort,
							 | 
						||
| 
								 | 
							
															DisplayMin:        "0",
							 | 
						||
| 
								 | 
							
														},
							 | 
						||
| 
								 | 
							
													},
							 | 
						||
| 
								 | 
							
												)
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									})
							 | 
						||
| 
								 | 
							
									return
							 | 
						||
| 
								 | 
							
								}
							 |