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