librato.go 7.6 KB

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