-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsampler.go
131 lines (117 loc) · 3.42 KB
/
sampler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// statsdig is a minimalist statsd client focused
// on integrating with sysdig cloud services.
// Although it can work with plain StatsD just fine.
package statsdig
import (
"fmt"
"net"
"strings"
"time"
)
// Tag represents a Sysdig StatsD tag, which is a extension
// to add more dimensions to your metric, like prometheus labels.
// More: https://support.sysdigcloud.com/hc/en-us/articles/204376099-Metrics-integrations-StatsD
type Tag struct {
Name string
Value string
}
// Sampler abstraction, makes it easy to have multiple implementations
// of a sampler, which can be useful to testing
type Sampler interface {
// Count sends a increment to a count metric with given name
Count(name string, tags ...Tag) error
// Gauge sets the gauge with the given name to the given value
Gauge(name string, value int, tags ...Tag) error
// Time sets duration in milliseconds with the given name to the given value
Time(name string, value time.Duration, tags ...Tag) error
}
// UDPSampler is a sampler that sends metrics through UDP
type UDPSampler struct {
conn net.PacketConn
addr *net.UDPAddr
}
func (s *UDPSampler) write(data []byte) (int, error) {
return s.conn.WriteTo(data, s.addr)
}
// NewSysdigSampler creates a sampler suited to work
// with the sysdig cloud client, sending metrics to localhost
// at the default statsd port.
func NewSysdigSampler() (*UDPSampler, error) {
return NewSampler("127.0.0.1:8125")
}
// NewSampler creates a sampler suited to work
// with any statsd server listening add the given addr,
// where addr must be serializeted just as the addr provided
// to Go's net.ResolveUDPAddr function.
func NewSampler(addr string) (*UDPSampler, error) {
udpAddr, err := net.ResolveUDPAddr("udp4", addr)
if err != nil {
return nil, fmt.Errorf("resolve udp address failed: %s", err)
}
conn, err := net.ListenPacket("udp4", ":0")
if err != nil {
return nil, fmt.Errorf("connection creation failed: %s", err)
}
return &UDPSampler{
conn: conn,
addr: udpAddr,
}, nil
}
// Count sends a counter metric as specified here:
// https://github.com/b/statsd_spec#counters
func (sampler *UDPSampler) Count(name string, tags ...Tag) error {
countType := "c"
message := serialize(name, 1, countType, tags...)
return sampler.send(message)
}
// Gauge sends a gauge metric as specified here:
// https://github.com/b/statsd_spec#gauges
func (sampler *UDPSampler) Gauge(name string, value int, tags ...Tag) error {
countType := "g"
message := serialize(name, value, countType, tags...)
return sampler.send(message)
}
// Time sends a time metric as specified here:
// https://github.com/b/statsd_spec#timers
func (sampler *UDPSampler) Time(name string, value time.Duration, tags ...Tag) error {
timeType := "ms"
ms := int(value / time.Millisecond)
message := serialize(name, ms, timeType, tags...)
return sampler.send(message)
}
func (sampler *UDPSampler) send(message []byte) error {
n, err := sampler.write(message)
if err != nil {
return err
}
if n != len(message) {
return fmt.Errorf(
"expected to send %d but sent %d",
len(message),
n,
)
}
return nil
}
func serialize(
name string,
value int,
metricType string,
tags ...Tag,
) []byte {
var strtags []string
for _, tag := range tags {
strtags = append(strtags, tag.Name+"="+tag.Value)
}
fulltags := ""
if len(strtags) > 0 {
fulltags += "#" + strings.Join(strtags, ",")
}
return []byte(fmt.Sprintf(
"%s%s:%d|%s",
name,
fulltags,
value,
metricType,
))
}