librato.go 7.6 KB

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