OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / go-kit / kit / metrics / graphite / graphite.go
1 // Package graphite provides a Graphite backend for metrics. Metrics are batched
2 // and emitted in the plaintext protocol. For more information, see
3 // http://graphite.readthedocs.io/en/latest/feeding-carbon.html#the-plaintext-protocol
4 //
5 // Graphite does not have a native understanding of metric parameterization, so
6 // label values not supported. Use distinct metrics for each unique combination
7 // of label values.
8 package graphite
9
10 import (
11         "fmt"
12         "io"
13         "sync"
14         "time"
15
16         "github.com/go-kit/kit/log"
17         "github.com/go-kit/kit/metrics"
18         "github.com/go-kit/kit/metrics/generic"
19         "github.com/go-kit/kit/util/conn"
20 )
21
22 // Graphite receives metrics observations and forwards them to a Graphite server.
23 // Create a Graphite object, use it to create metrics, and pass those metrics as
24 // dependencies to the components that will use them.
25 //
26 // All metrics are buffered until WriteTo is called. Counters and gauges are
27 // aggregated into a single observation per timeseries per write. Histograms are
28 // exploded into per-quantile gauges and reported once per write.
29 //
30 // To regularly report metrics to an io.Writer, use the WriteLoop helper method.
31 // To send to a Graphite server, use the SendLoop helper method.
32 type Graphite struct {
33         mtx        sync.RWMutex
34         prefix     string
35         counters   map[string]*Counter
36         gauges     map[string]*Gauge
37         histograms map[string]*Histogram
38         logger     log.Logger
39 }
40
41 // New returns a Graphite object that may be used to create metrics. Prefix is
42 // applied to all created metrics. Callers must ensure that regular calls to
43 // WriteTo are performed, either manually or with one of the helper methods.
44 func New(prefix string, logger log.Logger) *Graphite {
45         return &Graphite{
46                 prefix:     prefix,
47                 counters:   map[string]*Counter{},
48                 gauges:     map[string]*Gauge{},
49                 histograms: map[string]*Histogram{},
50                 logger:     logger,
51         }
52 }
53
54 // NewCounter returns a counter. Observations are aggregated and emitted once
55 // per write invocation.
56 func (g *Graphite) NewCounter(name string) *Counter {
57         c := NewCounter(g.prefix + name)
58         g.mtx.Lock()
59         g.counters[g.prefix+name] = c
60         g.mtx.Unlock()
61         return c
62 }
63
64 // NewGauge returns a gauge. Observations are aggregated and emitted once per
65 // write invocation.
66 func (g *Graphite) NewGauge(name string) *Gauge {
67         ga := NewGauge(g.prefix + name)
68         g.mtx.Lock()
69         g.gauges[g.prefix+name] = ga
70         g.mtx.Unlock()
71         return ga
72 }
73
74 // NewHistogram returns a histogram. Observations are aggregated and emitted as
75 // per-quantile gauges, once per write invocation. 50 is a good default value
76 // for buckets.
77 func (g *Graphite) NewHistogram(name string, buckets int) *Histogram {
78         h := NewHistogram(g.prefix+name, buckets)
79         g.mtx.Lock()
80         g.histograms[g.prefix+name] = h
81         g.mtx.Unlock()
82         return h
83 }
84
85 // WriteLoop is a helper method that invokes WriteTo to the passed writer every
86 // time the passed channel fires. This method blocks until the channel is
87 // closed, so clients probably want to run it in its own goroutine. For typical
88 // usage, create a time.Ticker and pass its C channel to this method.
89 func (g *Graphite) WriteLoop(c <-chan time.Time, w io.Writer) {
90         for range c {
91                 if _, err := g.WriteTo(w); err != nil {
92                         g.logger.Log("during", "WriteTo", "err", err)
93                 }
94         }
95 }
96
97 // SendLoop is a helper method that wraps WriteLoop, passing a managed
98 // connection to the network and address. Like WriteLoop, this method blocks
99 // until the channel is closed, so clients probably want to start it in its own
100 // goroutine. For typical usage, create a time.Ticker and pass its C channel to
101 // this method.
102 func (g *Graphite) SendLoop(c <-chan time.Time, network, address string) {
103         g.WriteLoop(c, conn.NewDefaultManager(network, address, g.logger))
104 }
105
106 // WriteTo flushes the buffered content of the metrics to the writer, in
107 // Graphite plaintext format. WriteTo abides best-effort semantics, so
108 // observations are lost if there is a problem with the write. Clients should be
109 // sure to call WriteTo regularly, ideally through the WriteLoop or SendLoop
110 // helper methods.
111 func (g *Graphite) WriteTo(w io.Writer) (count int64, err error) {
112         g.mtx.RLock()
113         defer g.mtx.RUnlock()
114         now := time.Now().Unix()
115
116         for name, c := range g.counters {
117                 n, err := fmt.Fprintf(w, "%s %f %d\n", name, c.c.ValueReset(), now)
118                 if err != nil {
119                         return count, err
120                 }
121                 count += int64(n)
122         }
123
124         for name, ga := range g.gauges {
125                 n, err := fmt.Fprintf(w, "%s %f %d\n", name, ga.g.Value(), now)
126                 if err != nil {
127                         return count, err
128                 }
129                 count += int64(n)
130         }
131
132         for name, h := range g.histograms {
133                 for _, p := range []struct {
134                         s string
135                         f float64
136                 }{
137                         {"50", 0.50},
138                         {"90", 0.90},
139                         {"95", 0.95},
140                         {"99", 0.99},
141                 } {
142                         n, err := fmt.Fprintf(w, "%s.p%s %f %d\n", name, p.s, h.h.Quantile(p.f), now)
143                         if err != nil {
144                                 return count, err
145                         }
146                         count += int64(n)
147                 }
148         }
149
150         return count, err
151 }
152
153 // Counter is a Graphite counter metric.
154 type Counter struct {
155         c *generic.Counter
156 }
157
158 // NewCounter returns a new usable counter metric.
159 func NewCounter(name string) *Counter {
160         return &Counter{generic.NewCounter(name)}
161 }
162
163 // With is a no-op.
164 func (c *Counter) With(...string) metrics.Counter { return c }
165
166 // Add implements counter.
167 func (c *Counter) Add(delta float64) { c.c.Add(delta) }
168
169 // Gauge is a Graphite gauge metric.
170 type Gauge struct {
171         g *generic.Gauge
172 }
173
174 // NewGauge returns a new usable Gauge metric.
175 func NewGauge(name string) *Gauge {
176         return &Gauge{generic.NewGauge(name)}
177 }
178
179 // With is a no-op.
180 func (g *Gauge) With(...string) metrics.Gauge { return g }
181
182 // Set implements gauge.
183 func (g *Gauge) Set(value float64) { g.g.Set(value) }
184
185 // Add implements metrics.Gauge.
186 func (g *Gauge) Add(delta float64) { g.g.Add(delta) }
187
188 // Histogram is a Graphite histogram metric. Observations are bucketed into
189 // per-quantile gauges.
190 type Histogram struct {
191         h *generic.Histogram
192 }
193
194 // NewHistogram returns a new usable Histogram metric.
195 func NewHistogram(name string, buckets int) *Histogram {
196         return &Histogram{generic.NewHistogram(name, buckets)}
197 }
198
199 // With is a no-op.
200 func (h *Histogram) With(...string) metrics.Histogram { return h }
201
202 // Observe implements histogram.
203 func (h *Histogram) Observe(value float64) { h.h.Observe(value) }