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	log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015")
42	for _ = range time.Tick(c.FlushInterval) {
43		if err := graphite(&c); nil != err {
44			log.Println(err)
45		}
46	}
47}
48
49// GraphiteOnce performs a single submission to Graphite, returning a
50// non-nil error on failed connections. This can be used in a loop
51// similar to GraphiteWithConfig for custom error handling.
52func GraphiteOnce(c GraphiteConfig) error {
53	log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015")
54	return graphite(&c)
55}
56
57func graphite(c *GraphiteConfig) error {
58	now := time.Now().Unix()
59	du := float64(c.DurationUnit)
60	conn, err := net.DialTCP("tcp", nil, c.Addr)
61	if nil != err {
62		return err
63	}
64	defer conn.Close()
65	w := bufio.NewWriter(conn)
66	c.Registry.Each(func(name string, i interface{}) {
67		switch metric := i.(type) {
68		case Counter:
69			fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, metric.Count(), now)
70		case Gauge:
71			fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Value(), now)
72		case GaugeFloat64:
73			fmt.Fprintf(w, "%s.%s.value %f %d\n", c.Prefix, name, metric.Value(), now)
74		case Histogram:
75			h := metric.Snapshot()
76			ps := h.Percentiles(c.Percentiles)
77			fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, h.Count(), now)
78			fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, h.Min(), now)
79			fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, h.Max(), now)
80			fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, h.Mean(), now)
81			fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, h.StdDev(), now)
82			for psIdx, psKey := range c.Percentiles {
83				key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1)
84				fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now)
85			}
86		case Meter:
87			m := metric.Snapshot()
88			fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, m.Count(), now)
89			fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, m.Rate1(), now)
90			fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, m.Rate5(), now)
91			fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, m.Rate15(), now)
92			fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, m.RateMean(), now)
93		case Timer:
94			t := metric.Snapshot()
95			ps := t.Percentiles(c.Percentiles)
96			fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, t.Count(), now)
97			fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, t.Min()/int64(du), now)
98			fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, t.Max()/int64(du), now)
99			fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, t.Mean()/du, now)
100			fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, t.StdDev()/du, now)
101			for psIdx, psKey := range c.Percentiles {
102				key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1)
103				fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now)
104			}
105			fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, t.Rate1(), now)
106			fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, t.Rate5(), now)
107			fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, t.Rate15(), now)
108			fmt.Fprintf(w, "%s.%s.mean-rate %.2f %d\n", c.Prefix, name, t.RateMean(), now)
109		}
110		w.Flush()
111	})
112	return nil
113}
114