2018-02-23 03:56:08 -06:00
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 ( )
}
2018-05-03 04:43:59 -05:00
func ( rep * Reporter ) Run ( ) {
2018-02-23 03:56:08 -06:00
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" )
2019-11-22 09:04:35 -06:00
ticker := time . NewTicker ( rep . Interval )
defer ticker . Stop ( )
2018-05-03 04:43:59 -05:00
metricsApi := & LibratoClient { rep . Email , rep . Token }
2019-11-22 09:04:35 -06:00
for now := range ticker . C {
2018-02-23 03:56:08 -06:00
var metrics Batch
var err error
2018-05-03 04:43:59 -05:00
if metrics , err = rep . BuildRequest ( now , rep . Registry ) ; err != nil {
2018-02-23 03:56:08 -06:00
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
}
2018-05-03 04:43:59 -05:00
func ( rep * Reporter ) BuildRequest ( now time . Time , r metrics . Registry ) ( snapshot Batch , err error ) {
2018-02-23 03:56:08 -06:00
snapshot = Batch {
// coerce timestamps to a stepping fn so that they line up in Librato graphs
2018-05-03 04:43:59 -05:00
MeasureTime : ( now . Unix ( ) / rep . intervalSec ) * rep . intervalSec ,
Source : rep . Source ,
2018-02-23 03:56:08 -06:00
}
snapshot . Gauges = make ( [ ] Measurement , 0 )
snapshot . Counters = make ( [ ] Measurement , 0 )
2018-05-03 04:43:59 -05:00
histogramGaugeCount := 1 + len ( rep . Percentiles )
2018-02-23 03:56:08 -06:00
r . Each ( func ( name string , metric interface { } ) {
2018-05-03 04:43:59 -05:00
if rep . Namespace != "" {
name = fmt . Sprintf ( "%s.%s" , rep . Namespace , name )
2018-02-23 03:56:08 -06:00
}
measurement := Measurement { }
2018-05-03 04:43:59 -05:00
measurement [ Period ] = rep . Interval . Seconds ( )
2018-02-23 03:56:08 -06:00
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 )
2023-03-23 08:13:50 -05:00
}
case metrics . CounterFloat64 :
if m . Count ( ) > 0 {
measurement [ Name ] = fmt . Sprintf ( "%s.%s" , name , "count" )
measurement [ Value ] = m . Count ( )
measurement [ Attributes ] = map [ string ] interface { } {
DisplayUnitsLong : Operations ,
DisplayUnitsShort : OperationsShort ,
DisplayMin : "0" ,
}
snapshot . Counters = append ( snapshot . Counters , measurement )
2018-02-23 03:56:08 -06:00
}
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
2018-05-03 04:43:59 -05:00
for i , p := range rep . Percentiles {
2018-02-23 03:56:08 -06:00
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 ( ) ,
2018-05-03 04:43:59 -05:00
Period : int64 ( rep . Interval . Seconds ( ) ) ,
2018-02-23 03:56:08 -06:00
Attributes : map [ string ] interface { } {
DisplayUnitsLong : Operations ,
DisplayUnitsShort : OperationsShort ,
DisplayMin : "0" ,
} ,
} ,
Measurement {
Name : fmt . Sprintf ( "%s.%s" , name , "5min" ) ,
Value : m . Rate5 ( ) ,
2018-05-03 04:43:59 -05:00
Period : int64 ( rep . Interval . Seconds ( ) ) ,
2018-02-23 03:56:08 -06:00
Attributes : map [ string ] interface { } {
DisplayUnitsLong : Operations ,
DisplayUnitsShort : OperationsShort ,
DisplayMin : "0" ,
} ,
} ,
Measurement {
Name : fmt . Sprintf ( "%s.%s" , name , "15min" ) ,
Value : m . Rate15 ( ) ,
2018-05-03 04:43:59 -05:00
Period : int64 ( rep . Interval . Seconds ( ) ) ,
2018-02-23 03:56:08 -06:00
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 ) ,
2018-05-03 04:43:59 -05:00
Period : int64 ( rep . Interval . Seconds ( ) ) ,
Attributes : rep . TimerAttributes ,
2018-02-23 03:56:08 -06:00
}
2018-05-03 04:43:59 -05:00
for i , p := range rep . Percentiles {
2018-02-23 03:56:08 -06:00
gauges [ i + 1 ] = Measurement {
Name : fmt . Sprintf ( "%s.timer.%2.0f" , name , p * 100 ) ,
Value : m . Percentile ( p ) ,
2018-05-03 04:43:59 -05:00
Period : int64 ( rep . Interval . Seconds ( ) ) ,
Attributes : rep . TimerAttributes ,
2018-02-23 03:56:08 -06:00
}
}
snapshot . Gauges = append ( snapshot . Gauges , gauges ... )
snapshot . Gauges = append ( snapshot . Gauges ,
Measurement {
Name : fmt . Sprintf ( "%s.%s" , name , "rate.1min" ) ,
Value : m . Rate1 ( ) ,
2018-05-03 04:43:59 -05:00
Period : int64 ( rep . Interval . Seconds ( ) ) ,
2018-02-23 03:56:08 -06:00
Attributes : map [ string ] interface { } {
DisplayUnitsLong : Operations ,
DisplayUnitsShort : OperationsShort ,
DisplayMin : "0" ,
} ,
} ,
Measurement {
Name : fmt . Sprintf ( "%s.%s" , name , "rate.5min" ) ,
Value : m . Rate5 ( ) ,
2018-05-03 04:43:59 -05:00
Period : int64 ( rep . Interval . Seconds ( ) ) ,
2018-02-23 03:56:08 -06:00
Attributes : map [ string ] interface { } {
DisplayUnitsLong : Operations ,
DisplayUnitsShort : OperationsShort ,
DisplayMin : "0" ,
} ,
} ,
Measurement {
Name : fmt . Sprintf ( "%s.%s" , name , "rate.15min" ) ,
Value : m . Rate15 ( ) ,
2018-05-03 04:43:59 -05:00
Period : int64 ( rep . Interval . Seconds ( ) ) ,
2018-02-23 03:56:08 -06:00
Attributes : map [ string ] interface { } {
DisplayUnitsLong : Operations ,
DisplayUnitsShort : OperationsShort ,
DisplayMin : "0" ,
} ,
} ,
)
}
}
} )
return
}