statsd-ruby 1.2.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|