statsd-ruby 1.2.1 → 1.3.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 +6 -14
- data/.travis.yml +18 -3
- data/README.rdoc +7 -2
- data/lib/statsd.rb +214 -10
- data/spec/helper.rb +11 -3
- data/spec/statsd_admin_spec.rb +117 -0
- data/spec/statsd_spec.rb +167 -26
- data/statsd-ruby.gemspec +1 -1
- metadata +18 -16
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
metadata.gz: !binary |-
|
9
|
-
OTNkNWQ4NTAwZTA5NzY2MDk3Njg5ZjJhODM2Mjg1MTZmYmE1YmNjNDg5Mjc0
|
10
|
-
YzU5OWZjMWIxY2JmOTBhYjMwMGM5NDIyNmUxYmE5MTllNmE3ZDNmMWEzNjll
|
11
|
-
MDY3NDc4MzU4ZDdlNGRhYjZjMTdiNTY0MGVlMjhkZmZiNjg5Yzk=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
ZmI2YzQyNDUxZWNjOGRmMjE0NjhiOGNjYTg4NjRlZjU2NDIxMjkyMmZmMzEx
|
14
|
-
YjBmZDA5MmZhY2RmODEzODk1NGY3ZmJmNjRhNWZjYmM5OTExYmY1MGI2OTNl
|
15
|
-
YmFjOTkwOTk4MjBmYmY4YWJkYTc5YWFiOTE2ZjE4MzgxYTBmODk=
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e4f5fc1d6357476dfd6402b233f22f43907c9fd7
|
4
|
+
data.tar.gz: 6210b9a5fc2b4221d0cf4cb3a1b11a4e9b6fdb14
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a71f111d63ae9d67eb355aa8f692bffa86336632a83e4f3947e148c208169ad379f2ef614498f9c89dec926dab79c5766c623b53fea38cb7a58976ad4c8b75c0
|
7
|
+
data.tar.gz: d4cb7379bf93758dec529e682f30724ff68a3d2f02c1acc3b6393256bebf0dcd04d988db05c3c346dae21901e63f6aa4092f47b6c98831e791a491acb3c59a97
|
data/.travis.yml
CHANGED
@@ -1,6 +1,21 @@
|
|
1
1
|
---
|
2
2
|
language: ruby
|
3
|
+
|
3
4
|
rvm:
|
4
|
-
- 1.
|
5
|
-
-
|
6
|
-
-
|
5
|
+
- 1.9.3
|
6
|
+
- 2.0.0
|
7
|
+
- 2.1
|
8
|
+
- 2.2
|
9
|
+
- 2.3.0
|
10
|
+
- rbx-2
|
11
|
+
- jruby
|
12
|
+
- jruby-head
|
13
|
+
- ruby-head
|
14
|
+
|
15
|
+
sudo: false
|
16
|
+
|
17
|
+
matrix:
|
18
|
+
allow_failures:
|
19
|
+
- rvm: rbx-2
|
20
|
+
- rvm: ruby-head
|
21
|
+
- rvm: jruby
|
data/README.rdoc
CHANGED
@@ -12,6 +12,9 @@ Bundler:
|
|
12
12
|
# Set up a global Statsd client for a server on localhost:9125
|
13
13
|
$statsd = Statsd.new 'localhost', 9125
|
14
14
|
|
15
|
+
# Set up a global Statsd client for a server on IPv6 port 9125
|
16
|
+
$statsd = Statsd.new '::1', 9125
|
17
|
+
|
15
18
|
# Send some stats
|
16
19
|
$statsd.increment 'garets'
|
17
20
|
$statsd.timing 'glork', 320
|
@@ -28,12 +31,14 @@ Bundler:
|
|
28
31
|
|
29
32
|
Run the specs with <tt>rake spec</tt>
|
30
33
|
|
31
|
-
Run the specs and include live integration specs with <tt>LIVE=true rake spec</tt>. Note: This will test over a real UDP socket.
|
32
|
-
|
33
34
|
= Performance
|
34
35
|
|
35
36
|
* A short note about DNS: If you use a dns name for the host option, then you will want to use a local caching dns service for optimial performance (e.g. nscd).
|
36
37
|
|
38
|
+
= Extensions / Libraries / Extra Docs
|
39
|
+
|
40
|
+
* See the wiki[https://github.com/reinh/statsd/wiki]
|
41
|
+
|
37
42
|
== Contributing to statsd
|
38
43
|
|
39
44
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
data/lib/statsd.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'forwardable'
|
3
|
+
require 'json'
|
3
4
|
|
4
5
|
# = Statsd: A Statsd client (https://github.com/etsy/statsd)
|
5
6
|
#
|
6
|
-
# @example Set up a global Statsd client for a server on localhost:
|
7
|
+
# @example Set up a global Statsd client for a server on localhost:8125
|
7
8
|
# $statsd = Statsd.new 'localhost', 8125
|
9
|
+
# @example Set up a global Statsd client for a server on IPv6 port 8125
|
10
|
+
# $statsd = Statsd.new '::1', 8125
|
8
11
|
# @example Send some stats
|
9
12
|
# $statsd.increment 'garets'
|
10
13
|
# $statsd.timing 'glork', 320
|
@@ -15,8 +18,8 @@ require 'forwardable'
|
|
15
18
|
# statsd = Statsd.new('localhost').tap{|sd| sd.namespace = 'account'}
|
16
19
|
# statsd.increment 'activate'
|
17
20
|
#
|
18
|
-
# Statsd instances are thread safe for general usage, by
|
19
|
-
#
|
21
|
+
# Statsd instances are thread safe for general usage, by utilizing the thread
|
22
|
+
# safe nature of UDP sends. The attributes are stateful, and are not
|
20
23
|
# mutexed, it is expected that users will not change these at runtime in
|
21
24
|
# threaded environments. If users require such use cases, it is recommend that
|
22
25
|
# users either mutex around their Statsd object, or create separate objects for
|
@@ -47,7 +50,8 @@ class Statsd
|
|
47
50
|
:host, :host=,
|
48
51
|
:port, :port=,
|
49
52
|
:prefix,
|
50
|
-
:postfix
|
53
|
+
:postfix,
|
54
|
+
:delimiter, :delimiter=
|
51
55
|
|
52
56
|
attr_accessor :batch_size
|
53
57
|
|
@@ -87,6 +91,146 @@ class Statsd
|
|
87
91
|
|
88
92
|
end
|
89
93
|
|
94
|
+
class Admin
|
95
|
+
# StatsD host. Defaults to 127.0.0.1.
|
96
|
+
attr_reader :host
|
97
|
+
|
98
|
+
# StatsD admin port. Defaults to 8126.
|
99
|
+
attr_reader :port
|
100
|
+
|
101
|
+
class << self
|
102
|
+
# Set to a standard logger instance to enable debug logging.
|
103
|
+
attr_accessor :logger
|
104
|
+
end
|
105
|
+
|
106
|
+
# @attribute [w] host.
|
107
|
+
# Users should call connect after changing this.
|
108
|
+
def host=(host)
|
109
|
+
@host = host || '127.0.0.1'
|
110
|
+
end
|
111
|
+
|
112
|
+
# @attribute [w] port.
|
113
|
+
# Users should call connect after changing this.
|
114
|
+
def port=(port)
|
115
|
+
@port = port || 8126
|
116
|
+
end
|
117
|
+
|
118
|
+
# @param [String] host your statsd host
|
119
|
+
# @param [Integer] port your statsd port
|
120
|
+
def initialize(host = '127.0.0.1', port = 8126)
|
121
|
+
@host = host || '127.0.0.1'
|
122
|
+
@port = port || 8126
|
123
|
+
# protects @socket transactions
|
124
|
+
@socket = nil
|
125
|
+
@s_mu = Mutex.new
|
126
|
+
connect
|
127
|
+
end
|
128
|
+
|
129
|
+
# Reads all gauges from StatsD.
|
130
|
+
def gauges
|
131
|
+
read_metric :gauges
|
132
|
+
end
|
133
|
+
|
134
|
+
# Reads all timers from StatsD.
|
135
|
+
def timers
|
136
|
+
read_metric :timers
|
137
|
+
end
|
138
|
+
|
139
|
+
# Reads all counters from StatsD.
|
140
|
+
def counters
|
141
|
+
read_metric :counters
|
142
|
+
end
|
143
|
+
|
144
|
+
# @param[String] item
|
145
|
+
# Deletes one or more gauges. Wildcards are allowed.
|
146
|
+
def delgauges item
|
147
|
+
delete_metric :gauges, item
|
148
|
+
end
|
149
|
+
|
150
|
+
# @param[String] item
|
151
|
+
# Deletes one or more timers. Wildcards are allowed.
|
152
|
+
def deltimers item
|
153
|
+
delete_metric :timers, item
|
154
|
+
end
|
155
|
+
|
156
|
+
# @param[String] item
|
157
|
+
# Deletes one or more counters. Wildcards are allowed.
|
158
|
+
def delcounters item
|
159
|
+
delete_metric :counters, item
|
160
|
+
end
|
161
|
+
|
162
|
+
def stats
|
163
|
+
result = @s_mu.synchronize do
|
164
|
+
# the format of "stats" isn't JSON, who knows why
|
165
|
+
send_to_socket "stats"
|
166
|
+
read_from_socket
|
167
|
+
end
|
168
|
+
items = {}
|
169
|
+
result.split("\n").each do |line|
|
170
|
+
key, val = line.chomp.split(": ")
|
171
|
+
items[key] = val.to_i
|
172
|
+
end
|
173
|
+
items
|
174
|
+
end
|
175
|
+
|
176
|
+
# Reconnects the socket, for when the statsd address may have changed. Users
|
177
|
+
# do not normally need to call this, but calling it may be appropriate when
|
178
|
+
# reconfiguring a process (e.g. from HUP)
|
179
|
+
def connect
|
180
|
+
@s_mu.synchronize do
|
181
|
+
begin
|
182
|
+
@socket.flush rescue nil
|
183
|
+
@socket.close if @socket
|
184
|
+
rescue
|
185
|
+
# Ignore socket errors on close.
|
186
|
+
end
|
187
|
+
@socket = TCPSocket.new(host, port)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
private
|
192
|
+
|
193
|
+
def read_metric name
|
194
|
+
result = @s_mu.synchronize do
|
195
|
+
send_to_socket name
|
196
|
+
read_from_socket
|
197
|
+
end
|
198
|
+
# for some reason, the reply looks like JSON, but isn't, quite
|
199
|
+
JSON.parse result.gsub("'", "\"")
|
200
|
+
end
|
201
|
+
|
202
|
+
def delete_metric name, item
|
203
|
+
result = @s_mu.synchronize do
|
204
|
+
send_to_socket "del#{name} #{item}"
|
205
|
+
read_from_socket
|
206
|
+
end
|
207
|
+
deleted = []
|
208
|
+
result.split("\n").each do |line|
|
209
|
+
deleted << line.chomp.split(": ")[-1]
|
210
|
+
end
|
211
|
+
deleted
|
212
|
+
end
|
213
|
+
|
214
|
+
def send_to_socket(message)
|
215
|
+
self.class.logger.debug { "Statsd: #{message}" } if self.class.logger
|
216
|
+
@socket.write(message.to_s + "\n")
|
217
|
+
rescue => boom
|
218
|
+
self.class.logger.error { "Statsd: #{boom.class} #{boom}" } if self.class.logger
|
219
|
+
nil
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
def read_from_socket
|
224
|
+
buffer = ""
|
225
|
+
loop do
|
226
|
+
line = @socket.readline
|
227
|
+
break if line == "END\n"
|
228
|
+
buffer += line
|
229
|
+
end
|
230
|
+
@socket.readline # clear the closing newline out of the socket
|
231
|
+
buffer
|
232
|
+
end
|
233
|
+
end
|
90
234
|
|
91
235
|
# A namespace to prepend to all statsd calls.
|
92
236
|
attr_reader :namespace
|
@@ -106,6 +250,9 @@ class Statsd
|
|
106
250
|
# a postfix to append to all metrics
|
107
251
|
attr_reader :postfix
|
108
252
|
|
253
|
+
# The replacement of :: on ruby module names when transformed to statsd metric names
|
254
|
+
attr_reader :delimiter
|
255
|
+
|
109
256
|
class << self
|
110
257
|
# Set to a standard logger instance to enable debug logging.
|
111
258
|
attr_accessor :logger
|
@@ -113,11 +260,18 @@ class Statsd
|
|
113
260
|
|
114
261
|
# @param [String] host your statsd host
|
115
262
|
# @param [Integer] port your statsd port
|
116
|
-
|
117
|
-
|
263
|
+
# @param [Symbol] :tcp for TCP, :udp or any other value for UDP
|
264
|
+
def initialize(host = '127.0.0.1', port = 8125, protocol = :udp)
|
265
|
+
@host = host || '127.0.0.1'
|
266
|
+
@port = port || 8125
|
267
|
+
self.delimiter = "."
|
118
268
|
@prefix = nil
|
119
269
|
@batch_size = 10
|
120
270
|
@postfix = nil
|
271
|
+
@socket = nil
|
272
|
+
@protocol = protocol || :udp
|
273
|
+
@s_mu = Mutex.new
|
274
|
+
connect
|
121
275
|
end
|
122
276
|
|
123
277
|
# @attribute [w] namespace
|
@@ -139,16 +293,24 @@ class Statsd
|
|
139
293
|
|
140
294
|
# @attribute [w] host
|
141
295
|
# Writes are not thread safe.
|
296
|
+
# Users should call hup after making changes.
|
142
297
|
def host=(host)
|
143
298
|
@host = host || '127.0.0.1'
|
144
299
|
end
|
145
300
|
|
146
301
|
# @attribute [w] port
|
147
302
|
# Writes are not thread safe.
|
303
|
+
# Users should call hup after making changes.
|
148
304
|
def port=(port)
|
149
305
|
@port = port || 8125
|
150
306
|
end
|
151
307
|
|
308
|
+
# @attribute [w] stat_delimiter
|
309
|
+
# Allows for custom delimiter replacement for :: when Ruby modules are transformed to statsd metric name
|
310
|
+
def delimiter=(delimiter)
|
311
|
+
@delimiter = delimiter || "."
|
312
|
+
end
|
313
|
+
|
152
314
|
# Sends an increment (count = 1) for the given stat to the statsd server.
|
153
315
|
#
|
154
316
|
# @param [String] stat stat name
|
@@ -230,6 +392,7 @@ class Statsd
|
|
230
392
|
def time(stat, sample_rate=1)
|
231
393
|
start = Time.now
|
232
394
|
result = yield
|
395
|
+
ensure
|
233
396
|
timing(stat, ((Time.now - start) * 1000).round, sample_rate)
|
234
397
|
result
|
235
398
|
end
|
@@ -245,14 +408,53 @@ class Statsd
|
|
245
408
|
# batch.gauge('user.count', User.count)
|
246
409
|
# end
|
247
410
|
def batch(&block)
|
248
|
-
Batch.new(self).easy
|
411
|
+
Batch.new(self).easy(&block)
|
412
|
+
end
|
413
|
+
|
414
|
+
# Reconnects the socket, useful if the address of the statsd has changed. This
|
415
|
+
# method is not thread safe from a perspective of stat submission. It is safe
|
416
|
+
# from resource leaks. Users do not normally need to call this, but calling it
|
417
|
+
# may be appropriate when reconfiguring a process (e.g. from HUP).
|
418
|
+
def connect
|
419
|
+
@s_mu.synchronize do
|
420
|
+
begin
|
421
|
+
@socket.close if @socket
|
422
|
+
rescue
|
423
|
+
# Errors are ignored on reconnects.
|
424
|
+
end
|
425
|
+
|
426
|
+
case @protocol
|
427
|
+
when :tcp
|
428
|
+
@socket = TCPSocket.new @host, @port
|
429
|
+
else
|
430
|
+
@socket = UDPSocket.new Addrinfo.ip(@host).afamily
|
431
|
+
@socket.connect host, port
|
432
|
+
end
|
433
|
+
end
|
249
434
|
end
|
250
435
|
|
251
436
|
protected
|
252
437
|
|
253
438
|
def send_to_socket(message)
|
254
439
|
self.class.logger.debug { "Statsd: #{message}" } if self.class.logger
|
255
|
-
|
440
|
+
|
441
|
+
retries = 0
|
442
|
+
n = 0
|
443
|
+
while true
|
444
|
+
# send(2) is atomic, however, in stream cases (TCP) the socket is left
|
445
|
+
# in an inconsistent state if a partial message is written. If that case
|
446
|
+
# occurs, the socket is closed down and we retry on a new socket.
|
447
|
+
n = socket.write(message)
|
448
|
+
|
449
|
+
if n == message.length
|
450
|
+
break
|
451
|
+
end
|
452
|
+
|
453
|
+
connect
|
454
|
+
retries += 1
|
455
|
+
raise "statsd: Failed to send after #{retries} attempts" if retries >= 5
|
456
|
+
end
|
457
|
+
n
|
256
458
|
rescue => boom
|
257
459
|
self.class.logger.error { "Statsd: #{boom.class} #{boom}" } if self.class.logger
|
258
460
|
nil
|
@@ -263,13 +465,15 @@ class Statsd
|
|
263
465
|
def send_stats(stat, delta, type, sample_rate=1)
|
264
466
|
if sample_rate == 1 or rand < sample_rate
|
265
467
|
# Replace Ruby module scoping with '.' and reserved chars (: | @) with underscores.
|
266
|
-
stat = stat.to_s.gsub('::',
|
468
|
+
stat = stat.to_s.gsub('::', delimiter).tr(':|@', '_')
|
267
469
|
rate = "|@#{sample_rate}" unless sample_rate == 1
|
268
470
|
send_to_socket "#{prefix}#{stat}#{postfix}:#{delta}|#{type}#{rate}"
|
269
471
|
end
|
270
472
|
end
|
271
473
|
|
272
474
|
def socket
|
273
|
-
|
475
|
+
# Subtle: If the socket is half-way through initialization in connect, it
|
476
|
+
# cannot be used yet.
|
477
|
+
@s_mu.synchronize { @socket } || raise(ThreadError, "socket missing")
|
274
478
|
end
|
275
479
|
end
|
data/spec/helper.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'bundler/setup'
|
2
|
-
require 'minitest/autorun'
|
3
2
|
|
4
3
|
require 'simplecov'
|
5
4
|
SimpleCov.start
|
6
5
|
|
6
|
+
require 'minitest/autorun'
|
7
7
|
require 'statsd'
|
8
8
|
require 'logger'
|
9
9
|
|
@@ -12,8 +12,9 @@ class FakeUDPSocket
|
|
12
12
|
@buffer = []
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
15
|
+
def write(message)
|
16
16
|
@buffer.push [message]
|
17
|
+
message.length
|
17
18
|
end
|
18
19
|
|
19
20
|
def recv
|
@@ -29,6 +30,13 @@ class FakeUDPSocket
|
|
29
30
|
end
|
30
31
|
|
31
32
|
def inspect
|
32
|
-
"
|
33
|
+
"<#{self.class.name}: #{@buffer.inspect}>"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class FakeTCPSocket < FakeUDPSocket
|
38
|
+
alias_method :readline, :recv
|
39
|
+
def write(message)
|
40
|
+
@buffer.push message
|
33
41
|
end
|
34
42
|
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
CHANGED
@@ -1,16 +1,28 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
describe Statsd do
|
4
|
-
class Statsd
|
5
|
-
public :socket
|
6
|
-
end
|
7
|
-
|
8
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
|
+
|
9
15
|
@statsd = Statsd.new('localhost', 1234)
|
10
|
-
@socket =
|
16
|
+
@socket = @statsd.instance_variable_set(:@socket, FakeUDPSocket.new)
|
11
17
|
end
|
12
18
|
|
13
|
-
after
|
19
|
+
after do
|
20
|
+
class Statsd
|
21
|
+
o, $VERBOSE = $VERBOSE, nil
|
22
|
+
alias connect connect_old
|
23
|
+
$VERBOSE = o
|
24
|
+
end
|
25
|
+
end
|
14
26
|
|
15
27
|
describe "#initialize" do
|
16
28
|
it "should set the host and port" do
|
@@ -23,6 +35,10 @@ describe Statsd do
|
|
23
35
|
statsd.host.must_equal '127.0.0.1'
|
24
36
|
statsd.port.must_equal 8125
|
25
37
|
end
|
38
|
+
|
39
|
+
it "should set delimiter to period by default" do
|
40
|
+
@statsd.delimiter.must_equal "."
|
41
|
+
end
|
26
42
|
end
|
27
43
|
|
28
44
|
describe "#host and #port" do
|
@@ -47,6 +63,23 @@ describe Statsd do
|
|
47
63
|
@statsd.port = nil
|
48
64
|
@statsd.port.must_equal 8125
|
49
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
|
50
83
|
end
|
51
84
|
|
52
85
|
describe "#increment" do
|
@@ -137,6 +170,18 @@ describe Statsd do
|
|
137
170
|
result.must_equal 'test'
|
138
171
|
end
|
139
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
|
+
|
140
185
|
describe "with a sample rate" do
|
141
186
|
before { class << @statsd; def rand; 0; end; end } # ensure delivery
|
142
187
|
|
@@ -273,6 +318,19 @@ describe Statsd do
|
|
273
318
|
@socket.recv.must_equal ['Statsd.SomeClass:1|c']
|
274
319
|
end
|
275
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, 1)
|
329
|
+
|
330
|
+
@socket.recv.must_equal ['Statsd-SomeOtherClass:1|c']
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
276
334
|
it "should replace statsd reserved chars in the stat name" do
|
277
335
|
@statsd.increment('ray@hostname.blah|blah.blah:blah', 1)
|
278
336
|
@socket.recv.must_equal ['ray_hostname.blah_blah.blah_blah:1|c']
|
@@ -283,7 +341,7 @@ describe Statsd do
|
|
283
341
|
before do
|
284
342
|
require 'stringio'
|
285
343
|
Statsd.logger = Logger.new(@log = StringIO.new)
|
286
|
-
@socket.instance_eval { def
|
344
|
+
@socket.instance_eval { def write(*) raise SocketError end }
|
287
345
|
end
|
288
346
|
|
289
347
|
it "should ignore socket errors" do
|
@@ -353,32 +411,115 @@ describe Statsd do
|
|
353
411
|
|
354
412
|
end
|
355
413
|
|
356
|
-
describe "
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
end
|
362
|
-
|
363
|
-
it "should create a new socket when used in a new thread" do
|
364
|
-
sock = @statsd.send(:socket)
|
365
|
-
Thread.new { Thread.current[:statsd_socket] }.value.wont_equal sock
|
414
|
+
describe "#connect" do
|
415
|
+
it "should reconnect" do
|
416
|
+
c = $connect_count
|
417
|
+
@statsd.connect
|
418
|
+
($connect_count - c).must_equal 1
|
366
419
|
end
|
367
|
-
|
368
420
|
end
|
421
|
+
|
369
422
|
end
|
370
423
|
|
371
424
|
describe Statsd do
|
372
425
|
describe "with a real UDP socket" do
|
373
426
|
it "should actually send stuff over the socket" do
|
374
|
-
|
375
|
-
|
376
|
-
|
427
|
+
family = Addrinfo.udp(UDPSocket.getaddress('localhost'), 0).afamily
|
428
|
+
begin
|
429
|
+
socket = UDPSocket.new family
|
430
|
+
host, port = 'localhost', 0
|
431
|
+
socket.bind(host, port)
|
432
|
+
port = socket.addr[1]
|
433
|
+
|
434
|
+
statsd = Statsd.new(host, port)
|
435
|
+
statsd.increment('foobar')
|
436
|
+
message = socket.recvfrom(16).first
|
437
|
+
message.must_equal 'foobar:1|c'
|
438
|
+
ensure
|
439
|
+
socket.close
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
it "should send stuff over an IPv4 socket" do
|
444
|
+
begin
|
445
|
+
socket = UDPSocket.new Socket::AF_INET
|
446
|
+
host, port = '127.0.0.1', 0
|
447
|
+
socket.bind(host, port)
|
448
|
+
port = socket.addr[1]
|
377
449
|
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
450
|
+
statsd = Statsd.new(host, port)
|
451
|
+
statsd.increment('foobar')
|
452
|
+
message = socket.recvfrom(16).first
|
453
|
+
message.must_equal 'foobar:1|c'
|
454
|
+
ensure
|
455
|
+
socket.close
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
it "should send stuff over an IPv6 socket" do
|
460
|
+
begin
|
461
|
+
socket = UDPSocket.new Socket::AF_INET6
|
462
|
+
host, port = '::1', 0
|
463
|
+
socket.bind(host, port)
|
464
|
+
port = socket.addr[1]
|
465
|
+
|
466
|
+
statsd = Statsd.new(host, port)
|
467
|
+
statsd.increment('foobar')
|
468
|
+
message = socket.recvfrom(16).first
|
469
|
+
message.must_equal 'foobar:1|c'
|
470
|
+
ensure
|
471
|
+
socket.close
|
472
|
+
end
|
382
473
|
end
|
383
474
|
end
|
384
|
-
|
475
|
+
|
476
|
+
describe "supports TCP sockets" do
|
477
|
+
it "should connect to and send stats over TCPv4" do
|
478
|
+
begin
|
479
|
+
host, port = '127.0.0.1', 0
|
480
|
+
server = TCPServer.new host, port
|
481
|
+
port = server.addr[1]
|
482
|
+
|
483
|
+
socket = nil
|
484
|
+
Thread.new { socket = server.accept }
|
485
|
+
|
486
|
+
statsd = Statsd.new(host, port, :tcp)
|
487
|
+
statsd.increment('foobar')
|
488
|
+
|
489
|
+
Timeout.timeout(5) do
|
490
|
+
Thread.pass while socket == nil
|
491
|
+
end
|
492
|
+
|
493
|
+
message = socket.recvfrom(16).first
|
494
|
+
message.must_equal 'foobar:1|c'
|
495
|
+
ensure
|
496
|
+
socket.close if socket
|
497
|
+
server.close
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
it "should connect to and send stats over TCPv6" do
|
502
|
+
begin
|
503
|
+
host, port = '::1', 0
|
504
|
+
server = TCPServer.new host, port
|
505
|
+
port = server.addr[1]
|
506
|
+
|
507
|
+
socket = nil
|
508
|
+
Thread.new { socket = server.accept }
|
509
|
+
|
510
|
+
statsd = Statsd.new(host, port, :tcp)
|
511
|
+
statsd.increment('foobar')
|
512
|
+
|
513
|
+
Timeout.timeout(5) do
|
514
|
+
Thread.pass while socket == nil
|
515
|
+
end
|
516
|
+
|
517
|
+
message = socket.recvfrom(16).first
|
518
|
+
message.must_equal 'foobar:1|c'
|
519
|
+
ensure
|
520
|
+
socket.close if socket
|
521
|
+
server.close
|
522
|
+
end
|
523
|
+
end
|
524
|
+
end
|
525
|
+
end
|
data/statsd-ruby.gemspec
CHANGED
metadata
CHANGED
@@ -1,69 +1,69 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: statsd-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rein Henrichs
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-01-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 3.2.0
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 3.2.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: yard
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: simplecov
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: 0.6.4
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 0.6.4
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
description: A Ruby StatsD client (https://github.com/etsy/statsd)
|
@@ -74,9 +74,9 @@ extra_rdoc_files:
|
|
74
74
|
- LICENSE.txt
|
75
75
|
- README.rdoc
|
76
76
|
files:
|
77
|
-
- .document
|
78
|
-
- .gitignore
|
79
|
-
- .travis.yml
|
77
|
+
- ".document"
|
78
|
+
- ".gitignore"
|
79
|
+
- ".travis.yml"
|
80
80
|
- Gemfile
|
81
81
|
- LICENSE.txt
|
82
82
|
- README.rdoc
|
@@ -84,6 +84,7 @@ files:
|
|
84
84
|
- lib/statsd-ruby.rb
|
85
85
|
- lib/statsd.rb
|
86
86
|
- spec/helper.rb
|
87
|
+
- spec/statsd_admin_spec.rb
|
87
88
|
- spec/statsd_spec.rb
|
88
89
|
- statsd-ruby.gemspec
|
89
90
|
homepage: https://github.com/reinh/statsd
|
@@ -96,21 +97,22 @@ require_paths:
|
|
96
97
|
- lib
|
97
98
|
required_ruby_version: !ruby/object:Gem::Requirement
|
98
99
|
requirements:
|
99
|
-
- -
|
100
|
+
- - ">="
|
100
101
|
- !ruby/object:Gem::Version
|
101
102
|
version: '0'
|
102
103
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
104
|
requirements:
|
104
|
-
- -
|
105
|
+
- - ">="
|
105
106
|
- !ruby/object:Gem::Version
|
106
107
|
version: '0'
|
107
108
|
requirements: []
|
108
109
|
rubyforge_project:
|
109
|
-
rubygems_version: 2.
|
110
|
+
rubygems_version: 2.5.1
|
110
111
|
signing_key:
|
111
112
|
specification_version: 4
|
112
113
|
summary: A Ruby StatsD client
|
113
114
|
test_files:
|
114
115
|
- spec/helper.rb
|
116
|
+
- spec/statsd_admin_spec.rb
|
115
117
|
- spec/statsd_spec.rb
|
116
118
|
has_rdoc:
|