forked from rs/xstats
-
Notifications
You must be signed in to change notification settings - Fork 0
/
xstats.go
193 lines (170 loc) · 5.21 KB
/
xstats.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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// Package xstats is a generic client for service instrumentation.
//
// xstats is inspired from Go-kit's metrics (https://github.com/go-kit/kit/tree/master/metrics)
// package but it takes a slightly different path. Instead of having to create
// an instance for each metric, xstats use a single instance to log every metrics
// you want. This reduces the boiler plate when you have a lot a metrics in your app.
// It's also easier in term of dependency injection.
//
// Talking about dependency injection, xstats comes with a xhandler.Handler
// integration so it can automatically inject the xstats client within the net/context
// of each request. Each request's xstats instance have its own tags storage ;
// This let you inject some per request contextual tags to be included with all
// observations sent within the lifespan of the request.
//
// xstats is pluggable and comes with integration for StatsD and DogStatsD,
// the Datadog (http://datadoghq.com) augmented version of StatsD with support for tags.
// More integration may come later (PR welcome).
package xstats // import "github.com/rs/xstats"
import (
"strings"
"sync"
"time"
)
const (
defaultDelimiter = "."
)
// XStater is a wrapper around a Sender to inject env tags within all observations.
type XStater interface {
Sender
// AddTag adds a tag to the request client, this tag will be sent with all
// subsequent stats queries.
AddTags(tags ...string)
// GetTags returns the tags associated with the XStater, all the tags that
// will be sent along with all the stats queries.
GetTags() []string
}
// Copier is an interface to an XStater that supports coping
type Copier interface {
Copy() XStater
}
// Scoper is an interface to an XStater, that supports scoping
type Scoper interface {
Scope(scope string, scopes ...string) XStater
}
var xstatsPool = sync.Pool{
New: func() interface{} {
return &xstats{}
},
}
// New returns a new xstats client with the provided backend sender.
func New(s Sender) XStater {
return NewPrefix(s, "")
}
// NewPrefix returns a new xstats client with the provided backend sender.
// The prefix is prepended to all metric names.
func NewPrefix(s Sender, prefix string) XStater {
return NewScoping(s, "", prefix)
}
// NewScoping returns a new xstats client with the provided backend sender.
// The delimiter is used to delimit scopes. Initial scopes can be provided.
func NewScoping(s Sender, delimiter string, scopes ...string) XStater {
xs := xstatsPool.Get().(*xstats)
xs.s = s
if len(scopes) > 0 {
xs.prefix = strings.Join(scopes, delimiter) + delimiter
} else {
xs.prefix = ""
}
xs.delimiter = delimiter
return xs
}
// Copy makes a copy of the given XStater if it implements the Copier
// interface. Otherwise it returns a nop stats.
func Copy(xs XStater) XStater {
if c, ok := xs.(Copier); ok {
return c.Copy()
}
return nop
}
// Scope makes a scoped copy of the given XStater if it implements the Scoper
// interface. Otherwise it returns a nop stats.
func Scope(xs XStater, scope string, scopes ...string) XStater {
if c, ok := xs.(Scoper); ok {
return c.Scope(scope, scopes...)
}
return nop
}
type xstats struct {
s Sender
// tags are appended to the tags provided to commands
tags []string
// prefix is prepended to all metric
prefix string
// delimiter is used to delimit scopes
delimiter string
}
// Copy implements the Copier interface
func (xs *xstats) Copy() XStater {
xs2 := NewScoping(xs.s, xs.delimiter, xs.prefix).(*xstats)
xs2.tags = xs.tags
return xs2
}
// Scope implements Scoper interface
func (xs *xstats) Scope(scope string, scopes ...string) XStater {
var scs []string
if xs.prefix == "" {
scs = make([]string, 0, 1+len(scopes))
} else {
scs = make([]string, 0, 2+len(scopes))
scs = append(scs, strings.TrimRight(xs.prefix, xs.delimiter))
}
scs = append(scs, scope)
scs = append(scs, scopes...)
xs2 := NewScoping(xs.s, xs.delimiter, scs...).(*xstats)
xs2.tags = xs.tags
return xs2
}
// Close returns the xstats to the sync.Pool.
func (xs *xstats) Close() error {
xs.s = nil
xs.tags = nil
xs.prefix = ""
xs.delimiter = ""
xstatsPool.Put(xs)
return nil
}
// AddTag implements XStater interface
func (xs *xstats) AddTags(tags ...string) {
if xs.tags == nil {
xs.tags = tags
} else {
xs.tags = append(xs.tags, tags...)
}
}
// AddTag implements XStater interface
func (xs *xstats) GetTags() []string {
return xs.tags
}
// Gauge implements XStater interface
func (xs *xstats) Gauge(stat string, value float64, tags ...string) {
if xs.s == nil {
return
}
tags = append(tags, xs.tags...)
xs.s.Gauge(xs.prefix+stat, value, tags...)
}
// Count implements XStater interface
func (xs *xstats) Count(stat string, count float64, tags ...string) {
if xs.s == nil {
return
}
tags = append(tags, xs.tags...)
xs.s.Count(xs.prefix+stat, count, tags...)
}
// Histogram implements XStater interface
func (xs *xstats) Histogram(stat string, value float64, tags ...string) {
if xs.s == nil {
return
}
tags = append(tags, xs.tags...)
xs.s.Histogram(xs.prefix+stat, value, tags...)
}
// Timing implements XStater interface
func (xs *xstats) Timing(stat string, duration time.Duration, tags ...string) {
if xs.s == nil {
return
}
tags = append(tags, xs.tags...)
xs.s.Timing(xs.prefix+stat, duration, tags...)
}