librato-statsd-ruby 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +43 -0
- data/.ruby-version +1 -0
- data/.travis.yml +21 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +20 -0
- data/README.md +47 -0
- data/Rakefile +15 -0
- data/lib/librato-statsd-ruby.rb +1 -0
- data/lib/statsd.rb +477 -0
- data/librato-statsd-ruby.gemspec +23 -0
- data/spec/helper.rb +43 -0
- data/spec/statsd_admin_spec.rb +117 -0
- data/spec/statsd_spec.rb +562 -0
- metadata +118 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new("librato-statsd-ruby", "1.0.0") do |s|
|
4
|
+
s.authors = ["Bryan Mikaelian"]
|
5
|
+
s.email = 'support@librato.com'
|
6
|
+
|
7
|
+
s.summary = "A Ruby StatsD client with Librato support"
|
8
|
+
|
9
|
+
s.homepage = "https://www.librato.com/docs/kb/collect/collection_agents/stastd.html"
|
10
|
+
s.licenses = %w[MIT]
|
11
|
+
|
12
|
+
s.extra_rdoc_files = %w[LICENSE.txt README.md]
|
13
|
+
|
14
|
+
if $0 =~ /gem/ # If running under rubygems (building), otherwise, just leave
|
15
|
+
s.files = `git ls-files`.split($\)
|
16
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
17
|
+
end
|
18
|
+
|
19
|
+
s.add_development_dependency "minitest", ">= 3.2.0"
|
20
|
+
s.add_development_dependency "yard"
|
21
|
+
s.add_development_dependency "simplecov", ">= 0.6.4"
|
22
|
+
s.add_development_dependency "rake"
|
23
|
+
end
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
|
3
|
+
require 'simplecov'
|
4
|
+
SimpleCov.start
|
5
|
+
|
6
|
+
require 'minitest/autorun'
|
7
|
+
require 'librato-statsd-ruby'
|
8
|
+
require 'logger'
|
9
|
+
require 'timeout'
|
10
|
+
|
11
|
+
class FakeUDPSocket
|
12
|
+
def initialize
|
13
|
+
@buffer = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def write(message)
|
17
|
+
@buffer.push [message]
|
18
|
+
message.length
|
19
|
+
end
|
20
|
+
|
21
|
+
def recv
|
22
|
+
@buffer.shift
|
23
|
+
end
|
24
|
+
|
25
|
+
def clear
|
26
|
+
@buffer = []
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
inspect
|
31
|
+
end
|
32
|
+
|
33
|
+
def inspect
|
34
|
+
"<#{self.class.name}: #{@buffer.inspect}>"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class FakeTCPSocket < FakeUDPSocket
|
39
|
+
alias_method :readline, :recv
|
40
|
+
def write(message)
|
41
|
+
@buffer.push message
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Statsd::Admin do
|
4
|
+
|
5
|
+
before do
|
6
|
+
class Statsd::Admin
|
7
|
+
o, $VERBOSE = $VERBOSE, nil
|
8
|
+
alias connect_old connect
|
9
|
+
def connect
|
10
|
+
$connect_count ||= 0
|
11
|
+
$connect_count += 1
|
12
|
+
end
|
13
|
+
$VERBOSE = o
|
14
|
+
end
|
15
|
+
@admin = Statsd::Admin.new('localhost', 1234)
|
16
|
+
@socket = @admin.instance_variable_set(:@socket, FakeTCPSocket.new)
|
17
|
+
end
|
18
|
+
|
19
|
+
after do
|
20
|
+
class Statsd::Admin
|
21
|
+
o, $VERBOSE = $VERBOSE, nil
|
22
|
+
alias connect connect_old
|
23
|
+
$VERBOSE = o
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#initialize" do
|
28
|
+
it "should set the host and port" do
|
29
|
+
@admin.host.must_equal 'localhost'
|
30
|
+
@admin.port.must_equal 1234
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should default the host to 127.0.0.1 and port to 8126" do
|
34
|
+
statsd = Statsd::Admin.new
|
35
|
+
statsd.host.must_equal '127.0.0.1'
|
36
|
+
statsd.port.must_equal 8126
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#host and #port" do
|
41
|
+
it "should set host and port" do
|
42
|
+
@admin.host = '1.2.3.4'
|
43
|
+
@admin.port = 5678
|
44
|
+
@admin.host.must_equal '1.2.3.4'
|
45
|
+
@admin.port.must_equal 5678
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should not resolve hostnames to IPs" do
|
49
|
+
@admin.host = 'localhost'
|
50
|
+
@admin.host.must_equal 'localhost'
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should set nil host to default" do
|
54
|
+
@admin.host = nil
|
55
|
+
@admin.host.must_equal '127.0.0.1'
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should set nil port to default" do
|
59
|
+
@admin.port = nil
|
60
|
+
@admin.port.must_equal 8126
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
%w(gauges counters timers).each do |action|
|
65
|
+
describe "##{action}" do
|
66
|
+
it "should send a command and return a Hash" do
|
67
|
+
["{'foo.bar': 0,\n",
|
68
|
+
"'foo.baz': 1,\n",
|
69
|
+
"'foo.quux': 2 }\n",
|
70
|
+
"END\n","\n"].each do |line|
|
71
|
+
@socket.write line
|
72
|
+
end
|
73
|
+
result = @admin.send action.to_sym
|
74
|
+
result.must_be_kind_of Hash
|
75
|
+
result.size.must_equal 3
|
76
|
+
@socket.readline.must_equal "#{action}\n"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "#del#{action}" do
|
81
|
+
it "should send a command and return an Array" do
|
82
|
+
["deleted: foo.bar\n",
|
83
|
+
"deleted: foo.baz\n",
|
84
|
+
"deleted: foo.quux\n",
|
85
|
+
"END\n", "\n"].each do |line|
|
86
|
+
@socket.write line
|
87
|
+
end
|
88
|
+
result = @admin.send "del#{action}", "foo.*"
|
89
|
+
result.must_be_kind_of Array
|
90
|
+
result.size.must_equal 3
|
91
|
+
@socket.readline.must_equal "del#{action} foo.*\n"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "#stats" do
|
97
|
+
it "should send a command and return a Hash" do
|
98
|
+
["whatever: 0\n", "END\n", "\n"].each do |line|
|
99
|
+
@socket.write line
|
100
|
+
end
|
101
|
+
result = @admin.stats
|
102
|
+
result.must_be_kind_of Hash
|
103
|
+
result["whatever"].must_equal 0
|
104
|
+
@socket.readline.must_equal "stats\n"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "#connect" do
|
109
|
+
it "should reconnect" do
|
110
|
+
c = $connect_count
|
111
|
+
@admin.connect
|
112
|
+
($connect_count - c).must_equal 1
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
|
data/spec/statsd_spec.rb
ADDED
@@ -0,0 +1,562 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Statsd do
|
4
|
+
before do
|
5
|
+
class Statsd
|
6
|
+
o, $VERBOSE = $VERBOSE, nil
|
7
|
+
alias connect_old connect
|
8
|
+
def connect
|
9
|
+
$connect_count ||= 1
|
10
|
+
$connect_count += 1
|
11
|
+
end
|
12
|
+
$VERBOSE = o
|
13
|
+
end
|
14
|
+
|
15
|
+
@statsd = Statsd.new('localhost', 1234)
|
16
|
+
@socket = @statsd.instance_variable_set(:@socket, FakeUDPSocket.new)
|
17
|
+
end
|
18
|
+
|
19
|
+
after do
|
20
|
+
class Statsd
|
21
|
+
o, $VERBOSE = $VERBOSE, nil
|
22
|
+
alias connect connect_old
|
23
|
+
$VERBOSE = o
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#initialize" do
|
28
|
+
it "should set the host and port" do
|
29
|
+
@statsd.host.must_equal 'localhost'
|
30
|
+
@statsd.port.must_equal 1234
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should default the host to 127.0.0.1 and port to 8125" do
|
34
|
+
statsd = Statsd.new
|
35
|
+
statsd.host.must_equal '127.0.0.1'
|
36
|
+
statsd.port.must_equal 8125
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should set delimiter to period by default" do
|
40
|
+
@statsd.delimiter.must_equal "."
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#host and #port" do
|
45
|
+
it "should set host and port" do
|
46
|
+
@statsd.host = '1.2.3.4'
|
47
|
+
@statsd.port = 5678
|
48
|
+
@statsd.host.must_equal '1.2.3.4'
|
49
|
+
@statsd.port.must_equal 5678
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should not resolve hostnames to IPs" do
|
53
|
+
@statsd.host = 'localhost'
|
54
|
+
@statsd.host.must_equal 'localhost'
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should set nil host to default" do
|
58
|
+
@statsd.host = nil
|
59
|
+
@statsd.host.must_equal '127.0.0.1'
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should set nil port to default" do
|
63
|
+
@statsd.port = nil
|
64
|
+
@statsd.port.must_equal 8125
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should allow an IPv6 address" do
|
68
|
+
@statsd.host = '::1'
|
69
|
+
@statsd.host.must_equal '::1'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#delimiter" do
|
74
|
+
it "should set delimiter" do
|
75
|
+
@statsd.delimiter = "-"
|
76
|
+
@statsd.delimiter.must_equal "-"
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should set default to period if not given a value" do
|
80
|
+
@statsd.delimiter = nil
|
81
|
+
@statsd.delimiter.must_equal "."
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "#increment" do
|
86
|
+
it "should format the message according to the statsd spec" do
|
87
|
+
@statsd.increment('foobar')
|
88
|
+
@socket.recv.must_equal ['foobar:1|c']
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "with a sample rate" do
|
92
|
+
before { class << @statsd; def rand; 0; end; end } # ensure delivery
|
93
|
+
it "should format the message according to the statsd spec" do
|
94
|
+
@statsd.increment('foobar', { sample_rate: 0.5 })
|
95
|
+
@socket.recv.must_equal ['foobar:1|c|@0.5']
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#decrement" do
|
101
|
+
it "should format the message according to the statsd spec" do
|
102
|
+
@statsd.decrement('foobar')
|
103
|
+
@socket.recv.must_equal ['foobar:-1|c']
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "with a sample rate" do
|
107
|
+
before { class << @statsd; def rand; 0; end; end } # ensure delivery
|
108
|
+
it "should format the message according to the statsd spec" do
|
109
|
+
@statsd.decrement('foobar', { sample_rate: 0.5 })
|
110
|
+
@socket.recv.must_equal ['foobar:-1|c|@0.5']
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "#gauge" do
|
116
|
+
it "should send a message with a 'g' type, per the nearbuy fork" do
|
117
|
+
@statsd.gauge('begrutten-suffusion', 536)
|
118
|
+
@socket.recv.must_equal ['begrutten-suffusion:536|g']
|
119
|
+
@statsd.gauge('begrutten-suffusion', -107.3)
|
120
|
+
@socket.recv.must_equal ['begrutten-suffusion:-107.3|g']
|
121
|
+
end
|
122
|
+
|
123
|
+
describe "with a sample rate" do
|
124
|
+
before { class << @statsd; def rand; 0; end; end } # ensure delivery
|
125
|
+
it "should format the message according to the statsd spec" do
|
126
|
+
@statsd.gauge('begrutten-suffusion', 536, { sample_rate: 0.1 })
|
127
|
+
@socket.recv.must_equal ['begrutten-suffusion:536|g|@0.1']
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "#timing" do
|
133
|
+
it "should format the message according to the statsd spec" do
|
134
|
+
@statsd.timing('foobar', 500)
|
135
|
+
@socket.recv.must_equal ['foobar:500|ms']
|
136
|
+
end
|
137
|
+
|
138
|
+
describe "with a sample rate" do
|
139
|
+
before { class << @statsd; def rand; 0; end; end } # ensure delivery
|
140
|
+
it "should format the message according to the statsd spec" do
|
141
|
+
@statsd.timing('foobar', 500, { sample_rate: 0.5 })
|
142
|
+
@socket.recv.must_equal ['foobar:500|ms|@0.5']
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe "#set" do
|
148
|
+
it "should format the message according to the statsd spec" do
|
149
|
+
@statsd.set('foobar', 765)
|
150
|
+
@socket.recv.must_equal ['foobar:765|s']
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "with a sample rate" do
|
154
|
+
before { class << @statsd; def rand; 0; end; end } # ensure delivery
|
155
|
+
it "should format the message according to the statsd spec" do
|
156
|
+
@statsd.set('foobar', 500, { sample_rate: 0.5 })
|
157
|
+
@socket.recv.must_equal ['foobar:500|s|@0.5']
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe "#time" do
|
163
|
+
it "should format the message according to the statsd spec" do
|
164
|
+
@statsd.time('foobar') { 'test' }
|
165
|
+
@socket.recv.must_equal ['foobar:0|ms']
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should return the result of the block" do
|
169
|
+
result = @statsd.time('foobar') { 'test' }
|
170
|
+
result.must_equal 'test'
|
171
|
+
end
|
172
|
+
|
173
|
+
describe "when given a block with an explicit return" do
|
174
|
+
it "should format the message according to the statsd spec" do
|
175
|
+
lambda { @statsd.time('foobar') { return 'test' } }.call
|
176
|
+
@socket.recv.must_equal ['foobar:0|ms']
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should return the result of the block" do
|
180
|
+
result = lambda { @statsd.time('foobar') { return 'test' } }.call
|
181
|
+
result.must_equal 'test'
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe "with a sample rate" do
|
186
|
+
before { class << @statsd; def rand; 0; end; end } # ensure delivery
|
187
|
+
|
188
|
+
it "should format the message according to the statsd spec" do
|
189
|
+
@statsd.time('foobar', { sample_rate: 0.5 }) { 'test' }
|
190
|
+
@socket.recv.must_equal ['foobar:0|ms|@0.5']
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
describe "#sampled" do
|
196
|
+
describe "when the sample rate is 1" do
|
197
|
+
before { class << @statsd; def rand; raise end; end }
|
198
|
+
it "should send" do
|
199
|
+
@statsd.timing('foobar', 500, { sample_rate: 1 })
|
200
|
+
@socket.recv.must_equal ['foobar:500|ms']
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe "when the sample rate is greater than a random value [0,1]" do
|
205
|
+
before { class << @statsd; def rand; 0; end; end } # ensure delivery
|
206
|
+
it "should send" do
|
207
|
+
@statsd.timing('foobar', 500, { sample_rate: 0.5 })
|
208
|
+
@socket.recv.must_equal ['foobar:500|ms|@0.5']
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
describe "when the sample rate is less than a random value [0,1]" do
|
213
|
+
before { class << @statsd; def rand; 1; end; end } # ensure no delivery
|
214
|
+
it "should not send" do
|
215
|
+
@statsd.timing('foobar', 500, { sample_rate: 0.5 }).must_equal nil
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe "when the sample rate is equal to a random value [0,1]" do
|
220
|
+
before { class << @statsd; def rand; 0; end; end } # ensure delivery
|
221
|
+
it "should send" do
|
222
|
+
@statsd.timing('foobar', 500, { sample_rate: 0.5 })
|
223
|
+
@socket.recv.must_equal ['foobar:500|ms|@0.5']
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
describe "with namespace" do
|
229
|
+
before { @statsd.namespace = 'service' }
|
230
|
+
|
231
|
+
it "should add namespace to increment" do
|
232
|
+
@statsd.increment('foobar')
|
233
|
+
@socket.recv.must_equal ['service.foobar:1|c']
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should add namespace to decrement" do
|
237
|
+
@statsd.decrement('foobar')
|
238
|
+
@socket.recv.must_equal ['service.foobar:-1|c']
|
239
|
+
end
|
240
|
+
|
241
|
+
it "should add namespace to timing" do
|
242
|
+
@statsd.timing('foobar', 500)
|
243
|
+
@socket.recv.must_equal ['service.foobar:500|ms']
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should add namespace to gauge" do
|
247
|
+
@statsd.gauge('foobar', 500)
|
248
|
+
@socket.recv.must_equal ['service.foobar:500|g']
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
describe "with postfix" do
|
253
|
+
before { @statsd.postfix = 'ip-23-45-56-78' }
|
254
|
+
|
255
|
+
it "should add postfix to increment" do
|
256
|
+
@statsd.increment('foobar')
|
257
|
+
@socket.recv.must_equal ['foobar.ip-23-45-56-78:1|c']
|
258
|
+
end
|
259
|
+
|
260
|
+
it "should add postfix to decrement" do
|
261
|
+
@statsd.decrement('foobar')
|
262
|
+
@socket.recv.must_equal ['foobar.ip-23-45-56-78:-1|c']
|
263
|
+
end
|
264
|
+
|
265
|
+
it "should add namespace to timing" do
|
266
|
+
@statsd.timing('foobar', 500)
|
267
|
+
@socket.recv.must_equal ['foobar.ip-23-45-56-78:500|ms']
|
268
|
+
end
|
269
|
+
|
270
|
+
it "should add namespace to gauge" do
|
271
|
+
@statsd.gauge('foobar', 500)
|
272
|
+
@socket.recv.must_equal ['foobar.ip-23-45-56-78:500|g']
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
describe '#postfix=' do
|
277
|
+
describe "when nil, false, or empty" do
|
278
|
+
it "should set postfix to nil" do
|
279
|
+
[nil, false, ''].each do |value|
|
280
|
+
@statsd.postfix = 'a postfix'
|
281
|
+
@statsd.postfix = value
|
282
|
+
@statsd.postfix.must_equal nil
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
describe "with logging" do
|
289
|
+
require 'stringio'
|
290
|
+
before { Statsd.logger = Logger.new(@log = StringIO.new)}
|
291
|
+
|
292
|
+
it "should write to the log in debug" do
|
293
|
+
Statsd.logger.level = Logger::DEBUG
|
294
|
+
|
295
|
+
@statsd.increment('foobar')
|
296
|
+
|
297
|
+
@log.string.must_match "Statsd: foobar:1|c"
|
298
|
+
end
|
299
|
+
|
300
|
+
it "should not write to the log unless debug" do
|
301
|
+
Statsd.logger.level = Logger::INFO
|
302
|
+
|
303
|
+
@statsd.increment('foobar')
|
304
|
+
|
305
|
+
@log.string.must_be_empty
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
describe "stat names" do
|
310
|
+
it "should accept anything as stat" do
|
311
|
+
@statsd.increment(Object)
|
312
|
+
end
|
313
|
+
|
314
|
+
it "should replace ruby constant delimeter with graphite package name" do
|
315
|
+
class Statsd::SomeClass; end
|
316
|
+
@statsd.increment(Statsd::SomeClass)
|
317
|
+
|
318
|
+
@socket.recv.must_equal ['Statsd.SomeClass:1|c']
|
319
|
+
end
|
320
|
+
|
321
|
+
describe "custom delimiter" do
|
322
|
+
before do
|
323
|
+
@statsd.delimiter = "-"
|
324
|
+
end
|
325
|
+
|
326
|
+
it "should replace ruby constant delimiter with custom delimiter" do
|
327
|
+
class Statsd::SomeOtherClass; end
|
328
|
+
@statsd.increment(Statsd::SomeOtherClass)
|
329
|
+
|
330
|
+
@socket.recv.must_equal ['Statsd-SomeOtherClass:1|c']
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
it "should replace statsd reserved chars in the stat name" do
|
335
|
+
@statsd.increment('ray@hostname.blah|blah.blah:blah')
|
336
|
+
@socket.recv.must_equal ['ray_hostname.blah_blah.blah_blah:1|c']
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
describe "handling socket errors" do
|
341
|
+
before do
|
342
|
+
require 'stringio'
|
343
|
+
Statsd.logger = Logger.new(@log = StringIO.new)
|
344
|
+
@socket.instance_eval { def write(*) raise SocketError end }
|
345
|
+
end
|
346
|
+
|
347
|
+
it "should ignore socket errors" do
|
348
|
+
@statsd.increment('foobar').must_equal nil
|
349
|
+
end
|
350
|
+
|
351
|
+
it "should log socket errors" do
|
352
|
+
@statsd.increment('foobar')
|
353
|
+
@log.string.must_match 'Statsd: SocketError'
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
describe "batching" do
|
358
|
+
it "should have a default batch size of 10" do
|
359
|
+
@statsd.batch_size.must_equal 10
|
360
|
+
end
|
361
|
+
|
362
|
+
it "should have a modifiable batch size" do
|
363
|
+
@statsd.batch_size = 7
|
364
|
+
@statsd.batch_size.must_equal 7
|
365
|
+
@statsd.batch do |b|
|
366
|
+
b.batch_size.must_equal 7
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
it "should flush the batch at the batch size or at the end of the block" do
|
371
|
+
@statsd.batch do |b|
|
372
|
+
b.batch_size = 3
|
373
|
+
|
374
|
+
# The first three should flush, the next two will be flushed when the
|
375
|
+
# block is done.
|
376
|
+
5.times { b.increment('foobar') }
|
377
|
+
|
378
|
+
@socket.recv.must_equal [(["foobar:1|c"] * 3).join("\n")]
|
379
|
+
end
|
380
|
+
|
381
|
+
@socket.recv.must_equal [(["foobar:1|c"] * 2).join("\n")]
|
382
|
+
end
|
383
|
+
|
384
|
+
it "should not flush to the socket if the backlog is empty" do
|
385
|
+
batch = Statsd::Batch.new(@statsd)
|
386
|
+
batch.flush
|
387
|
+
@socket.recv.must_be :nil?
|
388
|
+
|
389
|
+
batch.increment 'foobar'
|
390
|
+
batch.flush
|
391
|
+
@socket.recv.must_equal %w[foobar:1|c]
|
392
|
+
end
|
393
|
+
|
394
|
+
it "should support setting namespace for the underlying instance" do
|
395
|
+
batch = Statsd::Batch.new(@statsd)
|
396
|
+
batch.namespace = 'ns'
|
397
|
+
@statsd.namespace.must_equal 'ns'
|
398
|
+
end
|
399
|
+
|
400
|
+
it "should support setting host for the underlying instance" do
|
401
|
+
batch = Statsd::Batch.new(@statsd)
|
402
|
+
batch.host = '1.2.3.4'
|
403
|
+
@statsd.host.must_equal '1.2.3.4'
|
404
|
+
end
|
405
|
+
|
406
|
+
it "should support setting port for the underlying instance" do
|
407
|
+
batch = Statsd::Batch.new(@statsd)
|
408
|
+
batch.port = 42
|
409
|
+
@statsd.port.must_equal 42
|
410
|
+
end
|
411
|
+
|
412
|
+
end
|
413
|
+
|
414
|
+
describe "#connect" do
|
415
|
+
it "should reconnect" do
|
416
|
+
c = $connect_count
|
417
|
+
@statsd.connect
|
418
|
+
($connect_count - c).must_equal 1
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
describe "tags" do
|
423
|
+
before { class << @statsd; def rand; 0; end; end } # ensure delivery
|
424
|
+
|
425
|
+
describe "with counters" do
|
426
|
+
it "accepts tags" do
|
427
|
+
@statsd.increment('foobar', { tags: { foo: 'bar' }})
|
428
|
+
@socket.recv.must_equal ['foobar#foo=bar:1|c']
|
429
|
+
end
|
430
|
+
|
431
|
+
it "accepts tags with sample rate" do
|
432
|
+
@statsd.increment('foobar', { sample_rate: 0.5, tags: { foo: 'bar' }})
|
433
|
+
@socket.recv.must_equal ['foobar#foo=bar:1|c|@0.5']
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
describe "with timers" do
|
438
|
+
it "accepts tags" do
|
439
|
+
@statsd.time('foobar', { tags: { foo: 'bar' }}) {}
|
440
|
+
@socket.recv.must_equal ['foobar#foo=bar:0|ms']
|
441
|
+
end
|
442
|
+
|
443
|
+
it "accepts tags with sample rate" do
|
444
|
+
@statsd.time('foobar', { sample_rate: 0.5, tags: { foo: 'bar' }}) {}
|
445
|
+
@socket.recv.must_equal ['foobar#foo=bar:0|ms|@0.5']
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
describe "with postfix" do
|
450
|
+
before { @statsd.postfix = 'foo' }
|
451
|
+
|
452
|
+
it "accepts tags" do
|
453
|
+
@statsd.increment('foobar', { tags: { foo: 'bar' }})
|
454
|
+
@socket.recv.must_equal ['foobar.foo#foo=bar:1|c']
|
455
|
+
end
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
end
|
460
|
+
|
461
|
+
describe Statsd do
|
462
|
+
describe "with a real UDP socket" do
|
463
|
+
it "should actually send stuff over the socket" do
|
464
|
+
family = Addrinfo.udp(UDPSocket.getaddress('localhost'), 0).afamily
|
465
|
+
begin
|
466
|
+
socket = UDPSocket.new family
|
467
|
+
host, port = 'localhost', 0
|
468
|
+
socket.bind(host, port)
|
469
|
+
port = socket.addr[1]
|
470
|
+
|
471
|
+
statsd = Statsd.new(host, port)
|
472
|
+
statsd.increment('foobar')
|
473
|
+
message = socket.recvfrom(16).first
|
474
|
+
message.must_equal 'foobar:1|c'
|
475
|
+
ensure
|
476
|
+
socket.close
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
it "should send stuff over an IPv4 socket" do
|
481
|
+
begin
|
482
|
+
socket = UDPSocket.new Socket::AF_INET
|
483
|
+
host, port = '127.0.0.1', 0
|
484
|
+
socket.bind(host, port)
|
485
|
+
port = socket.addr[1]
|
486
|
+
|
487
|
+
statsd = Statsd.new(host, port)
|
488
|
+
statsd.increment('foobar')
|
489
|
+
message = socket.recvfrom(16).first
|
490
|
+
message.must_equal 'foobar:1|c'
|
491
|
+
ensure
|
492
|
+
socket.close
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
it "should send stuff over an IPv6 socket" do
|
497
|
+
begin
|
498
|
+
socket = UDPSocket.new Socket::AF_INET6
|
499
|
+
host, port = '::1', 0
|
500
|
+
socket.bind(host, port)
|
501
|
+
port = socket.addr[1]
|
502
|
+
|
503
|
+
statsd = Statsd.new(host, port)
|
504
|
+
statsd.increment('foobar')
|
505
|
+
message = socket.recvfrom(16).first
|
506
|
+
message.must_equal 'foobar:1|c'
|
507
|
+
ensure
|
508
|
+
socket.close
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
describe "supports TCP sockets" do
|
514
|
+
it "should connect to and send stats over TCPv4" do
|
515
|
+
begin
|
516
|
+
host, port = '127.0.0.1', 0
|
517
|
+
server = TCPServer.new host, port
|
518
|
+
port = server.addr[1]
|
519
|
+
|
520
|
+
socket = nil
|
521
|
+
Thread.new { socket = server.accept }
|
522
|
+
|
523
|
+
statsd = Statsd.new(host, port, :tcp)
|
524
|
+
statsd.increment('foobar')
|
525
|
+
|
526
|
+
Timeout.timeout(5) do
|
527
|
+
Thread.pass while socket == nil
|
528
|
+
end
|
529
|
+
|
530
|
+
message = socket.recvfrom(16).first
|
531
|
+
message.must_equal 'foobar:1|c'
|
532
|
+
ensure
|
533
|
+
socket.close if socket
|
534
|
+
server.close
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
it "should connect to and send stats over TCPv6" do
|
539
|
+
begin
|
540
|
+
host, port = '::1', 0
|
541
|
+
server = TCPServer.new host, port
|
542
|
+
port = server.addr[1]
|
543
|
+
|
544
|
+
socket = nil
|
545
|
+
Thread.new { socket = server.accept }
|
546
|
+
|
547
|
+
statsd = Statsd.new(host, port, :tcp)
|
548
|
+
statsd.increment('foobar')
|
549
|
+
|
550
|
+
Timeout.timeout(5) do
|
551
|
+
Thread.pass while socket == nil
|
552
|
+
end
|
553
|
+
|
554
|
+
message = socket.recvfrom(16).first
|
555
|
+
message.must_equal 'foobar:1|c'
|
556
|
+
ensure
|
557
|
+
socket.close if socket
|
558
|
+
server.close
|
559
|
+
end
|
560
|
+
end
|
561
|
+
end
|
562
|
+
end
|