1package metrics
2
3import (
4	"bufio"
5	"fmt"
6	"log"
7	"net"
8	"strconv"
9	"strings"
10	"time"
11)
12
13// GraphiteConfig provides a container with configuration parameters for
14// the Graphite exporter
15type GraphiteConfig struct {
16	Addr          *net.TCPAddr  // Network address to connect to
17	Registry      Registry      // Registry to be exported
18	FlushInterval time.Duration // Flush interval
19	DurationUnit  time.Duration // Time conversion unit for durations
20	Prefix        string        // Prefix to be prepended to metric names
21	Percentiles   []float64     // Percentiles to export from timers and histograms
22}
23
24// Graphite is a blocking exporter function which reports metrics in r
25// to a graphite server located at addr, flushing them every d duration
26// and prepending metric names with prefix.
27func Graphite(r Registry, d time.Duration, prefix string, addr *net.TCPAddr) {
28	GraphiteWithConfig(GraphiteConfig{
29		Addr:          addr,
30		Registry:      r,
31		FlushInterval: d,
32		DurationUnit:  time.Nanosecond,
33		Prefix:        prefix,
34		Percentiles:   []float64{0.5, 0.75, 0.95, 0.99, 0.999},
35	})
36}
37
38// GraphiteWithConfig is a blocking exporter function just like Graphite,
39// but it takes a GraphiteConfig instead.
40func GraphiteWithConfig(c GraphiteConfig) {
41	for _ = range time.Tick(c.FlushInterval) {
42		if err := graphite(&c); nil != err {
43			log.Println(err)
44		}
45	}
46}
47
48func graphite(c *GraphiteConfig) error {
49	now := time.Now().Unix()
50	du := float64(c.DurationUnit)
51	conn, err := net.DialTCP("tcp", nil, c.Addr)
52	if nil != err {
53		return err
54	}
55	defer conn.Close()
56	w := bufio.NewWriter(conn)
57	c.Registry.Each(func(name string, i interface{}) {
58		switch metric := i.(type) {
59		case Counter:
60			fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, metric.Count(), now)
61		case Gauge:
62			fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Value(), now)
63		case GaugeFloat64:
64			fmt.Fprintf(w, "%s.%s.value %f %d\n", c.Prefix, name, metric.Value(), now)
65		case Histogram:
66			h := metric.Snapshot()
67			ps := h.Percentiles(c.Percentiles)
68			fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, h.Count(), now)
69			fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, h.Min(), now)
70			fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, h.Max(), now)
71			fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, h.Mean(), now)
72			fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, h.StdDev(), now)
73			for psIdx, psKey := range c.Percentiles {
74				key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1)
75				fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now)
76			}
77		case Meter:
78			m := metric.Snapshot()
79			fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, m.Count(), now)
80			fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, m.Rate1(), now)
81			fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, m.Rate5(), now)
82			fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, m.Rate15(), now)
83			fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, m.RateMean(), now)
84		case Timer:
85			t := metric.Snapshot()
86			ps := t.Percentiles(c.Percentiles)
87			fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, t.Count(), now)
88			fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, int64(du)*t.Min(), now)
89			fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, int64(du)*t.Max(), now)
90			fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, du*t.Mean(), now)
91			fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, du*t.StdDev(), now)
92			for psIdx, psKey := range c.Percentiles {
93				key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1)
94				fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now)
95			}
96			fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, t.Rate1(), now)
97			fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, t.Rate5(), now)
98			fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, t.Rate15(), now)
99			fmt.Fprintf(w, "%s.%s.mean-rate %.2f %d\n", c.Prefix, name, t.RateMean(), now)
100		}
101		w.Flush()
102	})
103	return nil
104}
105