statsd-ruby 1.1.1 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +34 -0
- data/LICENSE.txt +1 -1
- data/README.rdoc +37 -12
- data/lib/statsd-ruby.rb +1 -0
- data/lib/statsd.rb +273 -17
- data/lib/statsd/monotonic_time.rb +35 -0
- data/spec/helper.rb +12 -3
- data/spec/statsd_admin_spec.rb +117 -0
- data/spec/statsd_spec.rb +321 -71
- data/statsd-ruby.gemspec +4 -4
- metadata +106 -93
- data/.travis.yml +0 -6
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0edd467226454eea7a63a7ed08e3468ac48a2d7438fdf8d291ab266a781c128b
|
4
|
+
data.tar.gz: 82f88de7bf7738e974a8d9f2d70f57f039d31d7f69f9b98c359b7d5c7970f93c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3520cc824d180d7304fa883fefa945d3f785d560f313c328fed8f28e4d799728c6ab61cd6b9a855d8023f644f2d328f7f6626e0af7770d8e83d99b2bb0e7a2f8
|
7
|
+
data.tar.gz: f0bc2ade747bee51cca11128a6d6ce21491b13354f519f582f90c94cd09ce1ee26372b52945c8d369dfc5160194cfd967644254350b0dd1567ff28cd64de8206
|
@@ -0,0 +1,34 @@
|
|
1
|
+
name: Ruby
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
strategy:
|
8
|
+
matrix:
|
9
|
+
os:
|
10
|
+
- ubuntu
|
11
|
+
- macos
|
12
|
+
ruby:
|
13
|
+
- 2.4
|
14
|
+
- 2.5
|
15
|
+
- 2.6
|
16
|
+
# TODO: - 2.7
|
17
|
+
# TODO: jruby, rbx
|
18
|
+
|
19
|
+
runs-on: ${{ matrix.os }}-latest
|
20
|
+
|
21
|
+
steps:
|
22
|
+
- uses: actions/checkout@v1
|
23
|
+
|
24
|
+
- name: Set up Ruby
|
25
|
+
uses: actions/setup-ruby@v1
|
26
|
+
with:
|
27
|
+
ruby-version: ${{ matrix.ruby }}
|
28
|
+
architecture: x64
|
29
|
+
|
30
|
+
- name: Build and test with Rake
|
31
|
+
run: |
|
32
|
+
gem install bundler
|
33
|
+
bundle install
|
34
|
+
bundle exec rake
|
data/LICENSE.txt
CHANGED
data/README.rdoc
CHANGED
@@ -1,17 +1,20 @@
|
|
1
|
-
= statsd-ruby {<img src="https://secure.travis-ci.org/reinh/statsd.
|
1
|
+
= statsd-ruby Travis: {<img src="https://secure.travis-ci.org/reinh/statsd.svg" />}[http://travis-ci.org/reinh/statsd] CI: {<img src="https://github.com/reinh/statsd/workflows/Ruby/badge.svg" />}[https://github.com/reinh/statsd/actions?query=workflow%3ARuby]
|
2
2
|
|
3
3
|
A Ruby client for {StatsD}[https://github.com/etsy/statsd]
|
4
4
|
|
5
5
|
= Installing
|
6
6
|
|
7
7
|
Bundler:
|
8
|
-
gem "statsd-ruby"
|
8
|
+
gem "statsd-ruby"
|
9
9
|
|
10
10
|
= Basic Usage
|
11
11
|
|
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,14 +31,16 @@ 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
|
-
* 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
|
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 optimal performance (e.g. nscd).
|
37
|
+
|
38
|
+
= Extensions / Libraries / Extra Docs
|
39
|
+
|
40
|
+
* See the wiki[https://github.com/reinh/statsd/wiki]
|
36
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
|
40
45
|
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
41
46
|
* Fork the project
|
@@ -47,15 +52,35 @@ Run the specs and include live integration specs with <tt>LIVE=true rake spec</t
|
|
47
52
|
== Contributors
|
48
53
|
|
49
54
|
* Rein Henrichs
|
50
|
-
*
|
55
|
+
* Alex Williams
|
56
|
+
* Andrew Meyer
|
57
|
+
* Chris Gaffney
|
58
|
+
* Cody Cutrer
|
59
|
+
* Corey Donohoe
|
60
|
+
* Dotan Nahum
|
61
|
+
* Erez Rabih
|
62
|
+
* Eric Chapweske
|
63
|
+
* Gabriel Burt
|
64
|
+
* Hannes Georg
|
65
|
+
* James Tucker
|
51
66
|
* Jeremy Kemper
|
67
|
+
* John Nunemaker
|
68
|
+
* Lann Martin
|
69
|
+
* Mahesh Murthy
|
70
|
+
* Manu J
|
71
|
+
* Matt Sanford
|
72
|
+
* Nate Bird
|
73
|
+
* Noah Lorang
|
74
|
+
* Oscar Del Ben
|
75
|
+
* Peter Mounce
|
76
|
+
* Ray Krueger
|
77
|
+
* Reed Lipman
|
78
|
+
* rick
|
52
79
|
* Ryan Tomayko
|
53
|
-
*
|
54
|
-
*
|
80
|
+
* Schuyler Erle
|
81
|
+
* Thomas Whaples
|
55
82
|
* Trae Robrock
|
56
|
-
* Corey Donohoe
|
57
|
-
* James Tucker
|
58
83
|
|
59
84
|
== Copyright
|
60
85
|
|
61
|
-
Copyright (c) 2011 Rein Henrichs. See LICENSE.txt for further details.
|
86
|
+
Copyright (c) 2011, 2012, 2013 Rein Henrichs. See LICENSE.txt for further details.
|
data/lib/statsd-ruby.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'statsd'
|
data/lib/statsd.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'forwardable'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
require 'statsd/monotonic_time'
|
3
6
|
|
4
7
|
# = Statsd: A Statsd client (https://github.com/etsy/statsd)
|
5
8
|
#
|
6
|
-
# @example Set up a global Statsd client for a server on localhost:
|
9
|
+
# @example Set up a global Statsd client for a server on localhost:8125
|
7
10
|
# $statsd = Statsd.new 'localhost', 8125
|
11
|
+
# @example Set up a global Statsd client for a server on IPv6 port 8125
|
12
|
+
# $statsd = Statsd.new '::1', 8125
|
8
13
|
# @example Send some stats
|
9
14
|
# $statsd.increment 'garets'
|
10
15
|
# $statsd.timing 'glork', 320
|
@@ -15,8 +20,8 @@ require 'forwardable'
|
|
15
20
|
# statsd = Statsd.new('localhost').tap{|sd| sd.namespace = 'account'}
|
16
21
|
# statsd.increment 'activate'
|
17
22
|
#
|
18
|
-
# Statsd instances are thread safe for general usage, by
|
19
|
-
#
|
23
|
+
# Statsd instances are thread safe for general usage, by utilizing the thread
|
24
|
+
# safe nature of UDP sends. The attributes are stateful, and are not
|
20
25
|
# mutexed, it is expected that users will not change these at runtime in
|
21
26
|
# threaded environments. If users require such use cases, it is recommend that
|
22
27
|
# users either mutex around their Statsd object, or create separate objects for
|
@@ -43,18 +48,27 @@ class Statsd
|
|
43
48
|
|
44
49
|
extend Forwardable
|
45
50
|
def_delegators :@statsd,
|
46
|
-
:namespace, :namespace=,
|
51
|
+
:namespace, :namespace=,
|
52
|
+
:host, :host=,
|
53
|
+
:port, :port=,
|
54
|
+
:prefix,
|
55
|
+
:postfix,
|
56
|
+
:delimiter, :delimiter=
|
47
57
|
|
48
|
-
attr_accessor :batch_size
|
58
|
+
attr_accessor :batch_size, :batch_byte_size, :flush_interval
|
49
59
|
|
50
|
-
# @param [Statsd] requires a configured Statsd instance
|
60
|
+
# @param [Statsd] statsd requires a configured Statsd instance
|
51
61
|
def initialize(statsd)
|
52
62
|
@statsd = statsd
|
53
63
|
@batch_size = statsd.batch_size
|
64
|
+
@batch_byte_size = statsd.batch_byte_size
|
65
|
+
@flush_interval = statsd.flush_interval
|
54
66
|
@backlog = []
|
67
|
+
@backlog_bytesize = 0
|
68
|
+
@last_flush = Time.now
|
55
69
|
end
|
56
70
|
|
57
|
-
# @
|
71
|
+
# @yield [Batch] yields itself
|
58
72
|
#
|
59
73
|
# A convenience method to ensure that data is not lost in the event of an
|
60
74
|
# exception being thrown. Batches will be transmitted on the parent socket
|
@@ -69,20 +83,178 @@ class Statsd
|
|
69
83
|
unless @backlog.empty?
|
70
84
|
@statsd.send_to_socket @backlog.join("\n")
|
71
85
|
@backlog.clear
|
86
|
+
@backlog_bytesize = 0
|
87
|
+
@last_flush = Time.now
|
72
88
|
end
|
73
89
|
end
|
74
90
|
|
75
91
|
protected
|
76
92
|
|
77
93
|
def send_to_socket(message)
|
94
|
+
# this message wouldn't fit; flush the queue. note that we don't have
|
95
|
+
# to do this for message based flushing, because we're incrementing by
|
96
|
+
# one, so the post-queue check will always catch it
|
97
|
+
if (@batch_byte_size && @backlog_bytesize + message.bytesize + 1 > @batch_byte_size) ||
|
98
|
+
(@flush_interval && last_flush_seconds_ago >= @flush_interval)
|
99
|
+
flush
|
100
|
+
end
|
78
101
|
@backlog << message
|
79
|
-
|
102
|
+
@backlog_bytesize += message.bytesize
|
103
|
+
# skip the interleaved newline for the first item
|
104
|
+
@backlog_bytesize += 1 if @backlog.length != 1
|
105
|
+
# if we're precisely full now, flush
|
106
|
+
if (@batch_size && @backlog.size == @batch_size) ||
|
107
|
+
(@batch_byte_size && @backlog_bytesize == @batch_byte_size)
|
80
108
|
flush
|
81
109
|
end
|
82
110
|
end
|
83
111
|
|
112
|
+
def last_flush_seconds_ago
|
113
|
+
Time.now - @last_flush
|
114
|
+
end
|
115
|
+
|
84
116
|
end
|
85
117
|
|
118
|
+
class Admin
|
119
|
+
# StatsD host. Defaults to 127.0.0.1.
|
120
|
+
attr_reader :host
|
121
|
+
|
122
|
+
# StatsD admin port. Defaults to 8126.
|
123
|
+
attr_reader :port
|
124
|
+
|
125
|
+
class << self
|
126
|
+
# Set to a standard logger instance to enable debug logging.
|
127
|
+
attr_accessor :logger
|
128
|
+
end
|
129
|
+
|
130
|
+
# @attribute [w] host.
|
131
|
+
# Users should call connect after changing this.
|
132
|
+
def host=(host)
|
133
|
+
@host = host || '127.0.0.1'
|
134
|
+
end
|
135
|
+
|
136
|
+
# @attribute [w] port.
|
137
|
+
# Users should call connect after changing this.
|
138
|
+
def port=(port)
|
139
|
+
@port = port || 8126
|
140
|
+
end
|
141
|
+
|
142
|
+
# @param [String] host your statsd host
|
143
|
+
# @param [Integer] port your statsd port
|
144
|
+
def initialize(host = '127.0.0.1', port = 8126)
|
145
|
+
@host = host || '127.0.0.1'
|
146
|
+
@port = port || 8126
|
147
|
+
# protects @socket transactions
|
148
|
+
@socket = nil
|
149
|
+
@s_mu = Mutex.new
|
150
|
+
connect
|
151
|
+
end
|
152
|
+
|
153
|
+
# Reads all gauges from StatsD.
|
154
|
+
def gauges
|
155
|
+
read_metric :gauges
|
156
|
+
end
|
157
|
+
|
158
|
+
# Reads all timers from StatsD.
|
159
|
+
def timers
|
160
|
+
read_metric :timers
|
161
|
+
end
|
162
|
+
|
163
|
+
# Reads all counters from StatsD.
|
164
|
+
def counters
|
165
|
+
read_metric :counters
|
166
|
+
end
|
167
|
+
|
168
|
+
# @param[String] item
|
169
|
+
# Deletes one or more gauges. Wildcards are allowed.
|
170
|
+
def delgauges item
|
171
|
+
delete_metric :gauges, item
|
172
|
+
end
|
173
|
+
|
174
|
+
# @param[String] item
|
175
|
+
# Deletes one or more timers. Wildcards are allowed.
|
176
|
+
def deltimers item
|
177
|
+
delete_metric :timers, item
|
178
|
+
end
|
179
|
+
|
180
|
+
# @param[String] item
|
181
|
+
# Deletes one or more counters. Wildcards are allowed.
|
182
|
+
def delcounters item
|
183
|
+
delete_metric :counters, item
|
184
|
+
end
|
185
|
+
|
186
|
+
def stats
|
187
|
+
result = @s_mu.synchronize do
|
188
|
+
# the format of "stats" isn't JSON, who knows why
|
189
|
+
send_to_socket "stats"
|
190
|
+
read_from_socket
|
191
|
+
end
|
192
|
+
items = {}
|
193
|
+
result.split("\n").each do |line|
|
194
|
+
key, val = line.chomp.split(": ")
|
195
|
+
items[key] = val.to_i
|
196
|
+
end
|
197
|
+
items
|
198
|
+
end
|
199
|
+
|
200
|
+
# Reconnects the socket, for when the statsd address may have changed. Users
|
201
|
+
# do not normally need to call this, but calling it may be appropriate when
|
202
|
+
# reconfiguring a process (e.g. from HUP)
|
203
|
+
def connect
|
204
|
+
@s_mu.synchronize do
|
205
|
+
begin
|
206
|
+
@socket.flush rescue nil
|
207
|
+
@socket.close if @socket
|
208
|
+
rescue
|
209
|
+
# Ignore socket errors on close.
|
210
|
+
end
|
211
|
+
@socket = TCPSocket.new(host, port)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
private
|
216
|
+
|
217
|
+
def read_metric name
|
218
|
+
result = @s_mu.synchronize do
|
219
|
+
send_to_socket name
|
220
|
+
read_from_socket
|
221
|
+
end
|
222
|
+
# for some reason, the reply looks like JSON, but isn't, quite
|
223
|
+
JSON.parse result.gsub("'", "\"")
|
224
|
+
end
|
225
|
+
|
226
|
+
def delete_metric name, item
|
227
|
+
result = @s_mu.synchronize do
|
228
|
+
send_to_socket "del#{name} #{item}"
|
229
|
+
read_from_socket
|
230
|
+
end
|
231
|
+
deleted = []
|
232
|
+
result.split("\n").each do |line|
|
233
|
+
deleted << line.chomp.split(": ")[-1]
|
234
|
+
end
|
235
|
+
deleted
|
236
|
+
end
|
237
|
+
|
238
|
+
def send_to_socket(message)
|
239
|
+
self.class.logger.debug { "Statsd: #{message}" } if self.class.logger
|
240
|
+
@socket.write(message.to_s + "\n")
|
241
|
+
rescue => boom
|
242
|
+
self.class.logger.error { "Statsd: #{boom.class} #{boom}" } if self.class.logger
|
243
|
+
nil
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
def read_from_socket
|
248
|
+
buffer = ""
|
249
|
+
loop do
|
250
|
+
line = @socket.readline
|
251
|
+
break if line == "END\n"
|
252
|
+
buffer += line
|
253
|
+
end
|
254
|
+
@socket.readline # clear the closing newline out of the socket
|
255
|
+
buffer
|
256
|
+
end
|
257
|
+
end
|
86
258
|
|
87
259
|
# A namespace to prepend to all statsd calls.
|
88
260
|
attr_reader :namespace
|
@@ -96,12 +268,21 @@ class Statsd
|
|
96
268
|
# StatsD namespace prefix, generated from #namespace
|
97
269
|
attr_reader :prefix
|
98
270
|
|
99
|
-
# The default batch size for new batches (default: 10)
|
271
|
+
# The default batch size for new batches. Set to nil to use batch_byte_size (default: 10)
|
100
272
|
attr_accessor :batch_size
|
101
273
|
|
274
|
+
# The default batch size, in bytes, for new batches (default: default nil; use batch_size)
|
275
|
+
attr_accessor :batch_byte_size
|
276
|
+
|
277
|
+
# The flush interval, in seconds, for new batches (default: nil)
|
278
|
+
attr_accessor :flush_interval
|
279
|
+
|
102
280
|
# a postfix to append to all metrics
|
103
281
|
attr_reader :postfix
|
104
282
|
|
283
|
+
# The replacement of :: on ruby module names when transformed to statsd metric names
|
284
|
+
attr_reader :delimiter
|
285
|
+
|
105
286
|
class << self
|
106
287
|
# Set to a standard logger instance to enable debug logging.
|
107
288
|
attr_accessor :logger
|
@@ -109,11 +290,20 @@ class Statsd
|
|
109
290
|
|
110
291
|
# @param [String] host your statsd host
|
111
292
|
# @param [Integer] port your statsd port
|
112
|
-
|
113
|
-
|
293
|
+
# @param [Symbol] protocol :tcp for TCP, :udp or any other value for UDP
|
294
|
+
def initialize(host = '127.0.0.1', port = 8125, protocol = :udp)
|
295
|
+
@host = host || '127.0.0.1'
|
296
|
+
@port = port || 8125
|
297
|
+
self.delimiter = "."
|
114
298
|
@prefix = nil
|
115
299
|
@batch_size = 10
|
300
|
+
@batch_byte_size = nil
|
301
|
+
@flush_interval = nil
|
116
302
|
@postfix = nil
|
303
|
+
@socket = nil
|
304
|
+
@protocol = protocol || :udp
|
305
|
+
@s_mu = Mutex.new
|
306
|
+
connect
|
117
307
|
end
|
118
308
|
|
119
309
|
# @attribute [w] namespace
|
@@ -135,16 +325,24 @@ class Statsd
|
|
135
325
|
|
136
326
|
# @attribute [w] host
|
137
327
|
# Writes are not thread safe.
|
328
|
+
# Users should call hup after making changes.
|
138
329
|
def host=(host)
|
139
330
|
@host = host || '127.0.0.1'
|
140
331
|
end
|
141
332
|
|
142
333
|
# @attribute [w] port
|
143
334
|
# Writes are not thread safe.
|
335
|
+
# Users should call hup after making changes.
|
144
336
|
def port=(port)
|
145
337
|
@port = port || 8125
|
146
338
|
end
|
147
339
|
|
340
|
+
# @attribute [w] stat_delimiter
|
341
|
+
# Allows for custom delimiter replacement for :: when Ruby modules are transformed to statsd metric name
|
342
|
+
def delimiter=(delimiter)
|
343
|
+
@delimiter = delimiter || "."
|
344
|
+
end
|
345
|
+
|
148
346
|
# Sends an increment (count = 1) for the given stat to the statsd server.
|
149
347
|
#
|
150
348
|
# @param [String] stat stat name
|
@@ -187,6 +385,22 @@ class Statsd
|
|
187
385
|
send_stats stat, value, :g, sample_rate
|
188
386
|
end
|
189
387
|
|
388
|
+
# Sends an arbitary set value for the given stat to the statsd server.
|
389
|
+
#
|
390
|
+
# This is for recording counts of unique events, which are useful to
|
391
|
+
# see on graphs to correlate to other values. For example, a deployment
|
392
|
+
# might get recorded as a set, and be drawn as annotations on a CPU history
|
393
|
+
# graph.
|
394
|
+
#
|
395
|
+
# @param [String] stat stat name.
|
396
|
+
# @param [Numeric] value event value.
|
397
|
+
# @param [Numeric] sample_rate sample rate, 1 for always
|
398
|
+
# @example Report a deployment happening:
|
399
|
+
# $statsd.set('deployment', DEPLOYMENT_EVENT_CODE)
|
400
|
+
def set(stat, value, sample_rate=1)
|
401
|
+
send_stats stat, value, :s, sample_rate
|
402
|
+
end
|
403
|
+
|
190
404
|
# Sends a timing (in ms) for the given stat to the statsd server. The
|
191
405
|
# sample_rate determines what percentage of the time this report is sent. The
|
192
406
|
# statsd server then uses the sample_rate to correctly track the average
|
@@ -208,9 +422,10 @@ class Statsd
|
|
208
422
|
# @example Report the time (in ms) taken to activate an account
|
209
423
|
# $statsd.time('account.activate') { @account.activate! }
|
210
424
|
def time(stat, sample_rate=1)
|
211
|
-
start =
|
425
|
+
start = MonotonicTime.time_in_ms
|
212
426
|
result = yield
|
213
|
-
|
427
|
+
ensure
|
428
|
+
timing(stat, (MonotonicTime.time_in_ms - start).round, sample_rate)
|
214
429
|
result
|
215
430
|
end
|
216
431
|
|
@@ -225,14 +440,53 @@ class Statsd
|
|
225
440
|
# batch.gauge('user.count', User.count)
|
226
441
|
# end
|
227
442
|
def batch(&block)
|
228
|
-
Batch.new(self).easy
|
443
|
+
Batch.new(self).easy(&block)
|
444
|
+
end
|
445
|
+
|
446
|
+
# Reconnects the socket, useful if the address of the statsd has changed. This
|
447
|
+
# method is not thread safe from a perspective of stat submission. It is safe
|
448
|
+
# from resource leaks. Users do not normally need to call this, but calling it
|
449
|
+
# may be appropriate when reconfiguring a process (e.g. from HUP).
|
450
|
+
def connect
|
451
|
+
@s_mu.synchronize do
|
452
|
+
begin
|
453
|
+
@socket.close if @socket
|
454
|
+
rescue
|
455
|
+
# Errors are ignored on reconnects.
|
456
|
+
end
|
457
|
+
|
458
|
+
case @protocol
|
459
|
+
when :tcp
|
460
|
+
@socket = TCPSocket.new @host, @port
|
461
|
+
else
|
462
|
+
@socket = UDPSocket.new Addrinfo.ip(@host).afamily
|
463
|
+
@socket.connect host, port
|
464
|
+
end
|
465
|
+
end
|
229
466
|
end
|
230
467
|
|
231
468
|
protected
|
232
469
|
|
233
470
|
def send_to_socket(message)
|
234
471
|
self.class.logger.debug { "Statsd: #{message}" } if self.class.logger
|
235
|
-
|
472
|
+
|
473
|
+
retries = 0
|
474
|
+
n = 0
|
475
|
+
while true
|
476
|
+
# send(2) is atomic, however, in stream cases (TCP) the socket is left
|
477
|
+
# in an inconsistent state if a partial message is written. If that case
|
478
|
+
# occurs, the socket is closed down and we retry on a new socket.
|
479
|
+
message = @protocol == :tcp ? message + "\n" : message
|
480
|
+
n = socket.write(message) rescue (err = $!; 0)
|
481
|
+
if n == message.length
|
482
|
+
break
|
483
|
+
end
|
484
|
+
|
485
|
+
connect
|
486
|
+
retries += 1
|
487
|
+
raise (err || "statsd: Failed to send after #{retries} attempts") if retries >= 5
|
488
|
+
end
|
489
|
+
n
|
236
490
|
rescue => boom
|
237
491
|
self.class.logger.error { "Statsd: #{boom.class} #{boom}" } if self.class.logger
|
238
492
|
nil
|
@@ -243,13 +497,15 @@ class Statsd
|
|
243
497
|
def send_stats(stat, delta, type, sample_rate=1)
|
244
498
|
if sample_rate == 1 or rand < sample_rate
|
245
499
|
# Replace Ruby module scoping with '.' and reserved chars (: | @) with underscores.
|
246
|
-
stat = stat.to_s.gsub('::',
|
500
|
+
stat = stat.to_s.gsub('::', delimiter).tr(':|@', '_')
|
247
501
|
rate = "|@#{sample_rate}" unless sample_rate == 1
|
248
502
|
send_to_socket "#{prefix}#{stat}#{postfix}:#{delta}|#{type}#{rate}"
|
249
503
|
end
|
250
504
|
end
|
251
505
|
|
252
506
|
def socket
|
253
|
-
|
507
|
+
# Subtle: If the socket is half-way through initialization in connect, it
|
508
|
+
# cannot be used yet.
|
509
|
+
@s_mu.synchronize { @socket } || raise(ThreadError, "socket missing")
|
254
510
|
end
|
255
511
|
end
|