librato.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. package librato
  2. import (
  3. "fmt"
  4. "github.com/rcrowley/go-metrics"
  5. "github.com/samuel/go-librato/librato"
  6. "log"
  7. "math"
  8. "time"
  9. )
  10. type LibratoReporter struct {
  11. Email, Token string
  12. Source string
  13. Interval time.Duration
  14. Registry metrics.Registry
  15. Percentiles []float64 // percentiles to report on histogram metrics
  16. }
  17. func Librato(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64) {
  18. reporter := &LibratoReporter{e, t, s, d, r, p}
  19. reporter.Run()
  20. }
  21. func (self *LibratoReporter) Run() {
  22. ticker := time.Tick(self.Interval * time.Millisecond)
  23. metricsApi := &librato.Metrics{self.Email, self.Token}
  24. for now := range ticker {
  25. var metrics *librato.MetricsFormat
  26. var err error
  27. if metrics, err = self.BuildRequest(now, self.Registry); err != nil {
  28. log.Printf("ERROR constructing librato request body %s", err)
  29. }
  30. if err := metricsApi.SendMetrics(metrics); err != nil {
  31. log.Printf("ERROR sending metrics to librato %s", err)
  32. }
  33. }
  34. }
  35. // calculate sum of squares from data provided by metrics.Histogram
  36. // see http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
  37. func sumSquares(m metrics.Histogram) float64 {
  38. count := float64(m.Count())
  39. sum := m.Mean() * float64(m.Count())
  40. sumSquared := math.Pow(float64(sum), 2)
  41. sumSquares := math.Pow(count*m.StdDev(), 2) + sumSquared/float64(m.Count())
  42. if math.IsNaN(sumSquares) {
  43. return 0.0
  44. }
  45. return sumSquared
  46. }
  47. func sumSquaresTimer(m metrics.Timer) float64 {
  48. count := float64(m.Count())
  49. sum := m.Mean() * float64(m.Count())
  50. sumSquared := math.Pow(float64(sum), 2)
  51. sumSquares := math.Pow(count*m.StdDev(), 2) + sumSquared/float64(m.Count())
  52. if math.IsNaN(sumSquares) {
  53. return 0.0
  54. }
  55. return sumSquares
  56. }
  57. func (self *LibratoReporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot *librato.MetricsFormat, err error) {
  58. snapshot = &librato.MetricsFormat{}
  59. snapshot.MeasureTime = now.Unix()
  60. snapshot.Source = self.Source
  61. snapshot.Gauges = make([]interface{}, 0)
  62. snapshot.Counters = make([]librato.Metric, 0)
  63. histogramGaugeCount := 1 + len(self.Percentiles)
  64. r.Each(func(name string, metric interface{}) {
  65. switch m := metric.(type) {
  66. case metrics.Counter:
  67. libratoName := fmt.Sprintf("%s.%s", name, "count")
  68. snapshot.Counters = append(snapshot.Counters, librato.Metric{Name: libratoName, Value: float64(m.Count())})
  69. case metrics.Gauge:
  70. snapshot.Gauges = append(snapshot.Gauges, librato.Metric{Name: name, Value: float64(m.Value())})
  71. case metrics.Histogram:
  72. if m.Count() > 0 {
  73. libratoName := fmt.Sprintf("%s.%s", name, "hist")
  74. gauges := make([]interface{}, histogramGaugeCount, histogramGaugeCount)
  75. gauges[0] = librato.Gauge{
  76. Name: libratoName,
  77. Count: uint64(m.Count()),
  78. Sum: m.Mean() * float64(m.Count()),
  79. Max: float64(m.Max()),
  80. Min: float64(m.Min()),
  81. SumSquares: sumSquares(m),
  82. }
  83. for i, p := range self.Percentiles {
  84. gauges[i+1] = librato.Metric{Name: fmt.Sprintf("%s.%.2f", libratoName, p), Value: m.Percentile(p)}
  85. }
  86. snapshot.Gauges = append(snapshot.Gauges, gauges...)
  87. }
  88. case metrics.Meter:
  89. snapshot.Counters = append(snapshot.Counters, librato.Metric{Name: name, Value: float64(m.Count())})
  90. snapshot.Gauges = append(snapshot.Gauges,
  91. librato.Metric{
  92. Name: fmt.Sprintf("%s.%s", name, "1min"),
  93. Value: m.Rate1(),
  94. },
  95. librato.Metric{
  96. Name: fmt.Sprintf("%s.%s", name, "5min"),
  97. Value: m.Rate5(),
  98. },
  99. librato.Metric{
  100. Name: fmt.Sprintf("%s.%s", name, "15min"),
  101. Value: m.Rate15(),
  102. },
  103. )
  104. case metrics.Timer:
  105. if m.Count() > 0 {
  106. libratoName := fmt.Sprintf("%s.%s", name, "timer")
  107. gauges := make([]interface{}, histogramGaugeCount, histogramGaugeCount)
  108. gauges[0] = librato.Gauge{
  109. Name: libratoName,
  110. Count: uint64(m.Count()),
  111. Sum: m.Mean() * float64(m.Count()),
  112. Max: float64(m.Max()),
  113. Min: float64(m.Min()),
  114. SumSquares: sumSquaresTimer(m),
  115. }
  116. for i, p := range self.Percentiles {
  117. gauges[i+1] = librato.Metric{Name: fmt.Sprintf("%s.%2.0f", libratoName, p*100), Value: m.Percentile(p)}
  118. }
  119. snapshot.Gauges = append(snapshot.Gauges, gauges...)
  120. snapshot.Gauges = append(snapshot.Gauges,
  121. librato.Metric{
  122. Name: fmt.Sprintf("%s.%s", name, "1min"),
  123. Value: m.Rate1(),
  124. },
  125. librato.Metric{
  126. Name: fmt.Sprintf("%s.%s", name, "5min"),
  127. Value: m.Rate5(),
  128. },
  129. librato.Metric{
  130. Name: fmt.Sprintf("%s.%s", name, "15min"),
  131. Value: m.Rate15(),
  132. },
  133. )
  134. }
  135. }
  136. })
  137. return
  138. }