dogstatsd-ruby 3.2.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -0
  3. data/lib/datadog/statsd.rb +264 -199
  4. metadata +5 -61
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f51c36199676261f46887363359562998810d87558ee38604dda36313f1a877f
4
- data.tar.gz: 8294b24d8021e02a2b0ac4e1e85cdd2f0c7ad78ca0b3b61fe7f38302baf1b2ae
3
+ metadata.gz: 6d2b1b9e7ec2a48c5305f6acda103d0b7a09787801dd899a38fb665f5389e4f3
4
+ data.tar.gz: 3d974fffb4ace3bc248e69dcdaabb4c556e6783a25d65af98cb65bdccafc52ee
5
5
  SHA512:
6
- metadata.gz: a3e82c961fca507847dabb5ca1cd19044dccd23a60aead380d9a7ce6c0b77537b85b0219a9a4a6d1b25242ac63321acb002d5ca88618c0656d706d33ffb5ffe3
7
- data.tar.gz: ec927e7597a0008cc689fb1d7bdef737cc2c7a0dcf6166b9c39bd1df4e455fbde525cd4269408e800e2ac56fd8563604d27b1015512af69223971f1d23033ad2
6
+ metadata.gz: 16c82cb62bfd324d5e1dc1c6293fa121b92bd0c88d00474210c5da67f2cb07ae350cc0b717b15739c7a6f09e9021323ba348cec70ec2d0ac66a362f15f81007b
7
+ data.tar.gz: 6e82cef56d746ecf7b10890d397e313c143611b18b4ae65db67eb2f252cb27acacb45de03f6447b7b4ebb1666209806d6b048fb2857c5c8790f1f6729f4ae806
data/README.md CHANGED
@@ -49,6 +49,11 @@ end
49
49
 
50
50
  # Tag a metric.
51
51
  statsd.histogram('query.time', 10, :tags => ["version:1"])
52
+
53
+ # Auto-close socket after end of block
54
+ Datadog::Statsd.open('localhost', 8125) do |s|
55
+ s.increment('page.views')
56
+ end
52
57
  ```
53
58
 
54
59
  You can also post events to your stream. You can tag them, set priority and even aggregate them with other events.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'socket'
2
3
 
3
4
  # = Datadog::Statsd: A DogStatsd client (https://www.datadoghq.com)
@@ -15,12 +16,137 @@ require 'socket'
15
16
  # statsd = Datadog::Statsd.new 'localhost', 8125, :namespace => 'account'
16
17
  # statsd.increment 'activate'
17
18
  # @example Create a statsd client with global tags
18
- # statsd = Datadog::Statsd.new 'localhost', 8125, :tags => 'tag1:true'
19
+ # statsd = Datadog::Statsd.new 'localhost', 8125, tags: 'tag1:true'
19
20
  module Datadog
20
21
  class Statsd
21
22
 
22
- DEFAULT_HOST = '127.0.0.1'
23
- DEFAULT_PORT = 8125
23
+ class Connection
24
+ DEFAULT_HOST = '127.0.0.1'
25
+ DEFAULT_PORT = 8125
26
+
27
+ # StatsD host. Defaults to 127.0.0.1.
28
+ attr_reader :host
29
+
30
+ # StatsD port. Defaults to 8125.
31
+ attr_reader :port
32
+
33
+ # DogStatsd unix socket path. Not used by default.
34
+ attr_reader :socket_path
35
+
36
+ def initialize(host, port, socket_path, logger)
37
+ @host = host || DEFAULT_HOST
38
+ @port = port || DEFAULT_PORT
39
+ @socket_path = socket_path
40
+ @logger = logger
41
+ end
42
+
43
+ def write(message)
44
+ @logger.debug { "Statsd: #{message}" } if @logger
45
+ if @socket_path.nil?
46
+ socket.send(message, 0)
47
+ else
48
+ socket.sendmsg_nonblock(message)
49
+ end
50
+ rescue StandardError => boom
51
+ # Give up on this socket if it looks like it is bad
52
+ bad_socket = !@socket_path.nil? && (
53
+ boom.is_a?(Errno::ECONNREFUSED) ||
54
+ boom.is_a?(Errno::ECONNRESET) ||
55
+ boom.is_a?(Errno::ENOENT)
56
+ )
57
+ if bad_socket
58
+ @socket = nil
59
+ return
60
+ end
61
+
62
+ # Try once to reconnect if the socket has been closed
63
+ retries ||= 1
64
+ if retries <= 1 && boom.is_a?(IOError) && boom.message =~ /closed stream/i
65
+ retries += 1
66
+ begin
67
+ @socket = connect
68
+ retry
69
+ rescue StandardError => e
70
+ boom = e
71
+ end
72
+ end
73
+
74
+ @logger.error { "Statsd: #{boom.class} #{boom}" } if @logger
75
+ nil
76
+ end
77
+
78
+ # Close the underlying socket
79
+ def close
80
+ @socket && @socket.close
81
+ end
82
+
83
+ private
84
+
85
+ def socket
86
+ @socket ||= connect
87
+ end
88
+
89
+ def connect
90
+ if @socket_path.nil?
91
+ socket = UDPSocket.new
92
+ socket.connect(@host, @port)
93
+ else
94
+ socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM)
95
+ socket.connect(Socket.pack_sockaddr_un(@socket_path))
96
+ end
97
+ socket
98
+ end
99
+ end
100
+
101
+ class Batch
102
+ def initialize(connection, max_buffer_bytes)
103
+ @connection = connection
104
+ @max_buffer_bytes = max_buffer_bytes
105
+ @depth = 0
106
+ reset
107
+ end
108
+
109
+ def open
110
+ @depth += 1
111
+ yield
112
+ ensure
113
+ @depth -= 1
114
+ flush if !open?
115
+ end
116
+
117
+ def open?
118
+ @depth > 0
119
+ end
120
+
121
+ def add(message)
122
+ message_bytes = message.bytesize
123
+
124
+ unless @buffer_bytes == 0
125
+ if @buffer_bytes + 1 + message_bytes >= @max_buffer_bytes
126
+ flush
127
+ else
128
+ @buffer << NEW_LINE
129
+ @buffer_bytes += 1
130
+ end
131
+ end
132
+
133
+ @buffer << message
134
+ @buffer_bytes += message_bytes
135
+ end
136
+
137
+ def flush
138
+ return if @buffer_bytes == 0
139
+ @connection.write @buffer
140
+ reset
141
+ end
142
+
143
+ private
144
+
145
+ def reset
146
+ @buffer = String.new
147
+ @buffer_bytes = 0
148
+ end
149
+ end
24
150
 
25
151
  # Create a dictionary to assign a key to every parameter's name, except for tags (treated differently)
26
152
  # Goal: Simple and fast to add some other parameters
@@ -46,75 +172,66 @@ module Datadog
46
172
  CRITICAL = 2
47
173
  UNKNOWN = 3
48
174
 
175
+ MAX_EVENT_SIZE = 8 * 1024
176
+
49
177
  COUNTER_TYPE = 'c'.freeze
50
178
  GAUGE_TYPE = 'g'.freeze
51
179
  HISTOGRAM_TYPE = 'h'.freeze
180
+ DISTRIBUTION_TYPE = 'd'.freeze
52
181
  TIMING_TYPE = 'ms'.freeze
53
182
  SET_TYPE = 's'.freeze
183
+ VERSION = "4.0.0".freeze
54
184
 
55
185
  # A namespace to prepend to all statsd calls. Defaults to no namespace.
56
186
  attr_reader :namespace
57
187
 
58
- # StatsD host. Defaults to 127.0.0.1.
59
- attr_reader :host
60
-
61
- # StatsD port. Defaults to 8125.
62
- attr_reader :port
63
-
64
- # DogStatsd unix socket path. Not used by default.
65
- attr_reader :socket_path
66
-
67
188
  # Global tags to be added to every statsd call. Defaults to no tags.
68
189
  attr_reader :tags
69
190
 
70
191
  # Buffer containing the statsd message before they are sent in batch
71
192
  attr_reader :buffer
72
193
 
73
- # Maximum number of metrics in the buffer before it is flushed
74
- attr_accessor :max_buffer_size
194
+ # Maximum buffer size in bytes before it is flushed
195
+ attr_reader :max_buffer_bytes
75
196
 
76
- class << self
77
- # Set to a standard logger instance to enable debug logging.
78
- attr_accessor :logger
79
- end
80
-
81
- # Return the current version of the library.
82
- def self.VERSION
83
- "3.2.0"
84
- end
197
+ # Connection
198
+ attr_reader :connection
85
199
 
86
200
  # @param [String] host your statsd host
87
201
  # @param [Integer] port your statsd port
88
- # @option opts [String] :namespace set a namespace to be prepended to every metric name
89
- # @option opts [Array<String>] :tags tags to be added to every metric
90
- def initialize(host = DEFAULT_HOST, port = DEFAULT_PORT, opts = {}, max_buffer_size=50)
91
- self.host, self.port = host, port
92
- @socket_path = opts[:socket_path]
93
- @prefix = nil
94
- @socket = connect_to_socket if @socket_path.nil?
95
- self.namespace = opts[:namespace]
96
- self.tags = opts[:tags]
97
- @buffer = Array.new
98
- self.max_buffer_size = max_buffer_size
99
- @batch_nesting_depth = 0
100
- end
202
+ # @option [String] namespace set a namespace to be prepended to every metric name
203
+ # @option [Array<String>] tags tags to be added to every metric
204
+ # @option [Loger] logger for debugging
205
+ # @option [Integer] max_buffer_bytes max bytes to buffer when using #batch
206
+ # @option [String] socket_path unix socket path
207
+ def initialize(
208
+ host = nil,
209
+ port = nil,
210
+ namespace: nil,
211
+ tags: nil,
212
+ max_buffer_bytes: 8192,
213
+ socket_path: nil,
214
+ logger: nil
215
+ )
216
+ @connection = Connection.new(host, port, socket_path, logger)
217
+ @logger = logger
101
218
 
102
- def namespace=(namespace) #:nodoc:
103
219
  @namespace = namespace
104
- @prefix = namespace.nil? ? nil : "#{namespace}.".freeze
105
- end
220
+ @prefix = @namespace ? "#{@namespace}.".freeze : nil
106
221
 
107
- def host=(host) #:nodoc:
108
- @host = host || DEFAULT_HOST
109
- end
222
+ raise ArgumentError, 'tags must be a Array<String>' unless tags.nil? or tags.is_a? Array
223
+ @tags = (tags || []).compact.map! {|tag| escape_tag_content(tag)}
110
224
 
111
- def port=(port) #:nodoc:
112
- @port = port || DEFAULT_PORT
225
+ @batch = Batch.new @connection, max_buffer_bytes
113
226
  end
114
227
 
115
- def tags=(tags) #:nodoc:
116
- raise ArgumentError, 'tags must be a Array<String>' unless tags.nil? or tags.is_a? Array
117
- @tags = (tags || []).compact.map! {|tag| escape_tag_content(tag)}
228
+ # yield a new instance to a block and close it when done
229
+ # for short-term use-cases that don't want to close the socket manually
230
+ def self.open(*args)
231
+ instance = new(*args)
232
+ yield instance
233
+ ensure
234
+ instance.close
118
235
  end
119
236
 
120
237
  # Sends an increment (count = 1) for the given stat to the statsd server.
@@ -125,7 +242,7 @@ module Datadog
125
242
  # @option opts [Array<String>] :tags An array of tags
126
243
  # @option opts [Numeric] :by increment value, default 1
127
244
  # @see #count
128
- def increment(stat, opts={})
245
+ def increment(stat, opts=EMPTY_OPTIONS)
129
246
  opts = {:sample_rate => opts} if opts.is_a? Numeric
130
247
  incr_value = opts.fetch(:by, 1)
131
248
  count stat, incr_value, opts
@@ -139,7 +256,7 @@ module Datadog
139
256
  # @option opts [Array<String>] :tags An array of tags
140
257
  # @option opts [Numeric] :by decrement value, default 1
141
258
  # @see #count
142
- def decrement(stat, opts={})
259
+ def decrement(stat, opts=EMPTY_OPTIONS)
143
260
  opts = {:sample_rate => opts} if opts.is_a? Numeric
144
261
  decr_value = - opts.fetch(:by, 1)
145
262
  count stat, decr_value, opts
@@ -152,7 +269,7 @@ module Datadog
152
269
  # @param [Hash] opts the options to create the metric with
153
270
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
154
271
  # @option opts [Array<String>] :tags An array of tags
155
- def count(stat, count, opts={})
272
+ def count(stat, count, opts=EMPTY_OPTIONS)
156
273
  opts = {:sample_rate => opts} if opts.is_a? Numeric
157
274
  send_stats stat, count, COUNTER_TYPE, opts
158
275
  end
@@ -170,7 +287,7 @@ module Datadog
170
287
  # @option opts [Array<String>] :tags An array of tags
171
288
  # @example Report the current user count:
172
289
  # $statsd.gauge('user.count', User.count)
173
- def gauge(stat, value, opts={})
290
+ def gauge(stat, value, opts=EMPTY_OPTIONS)
174
291
  opts = {:sample_rate => opts} if opts.is_a? Numeric
175
292
  send_stats stat, value, GAUGE_TYPE, opts
176
293
  end
@@ -184,10 +301,26 @@ module Datadog
184
301
  # @option opts [Array<String>] :tags An array of tags
185
302
  # @example Report the current user count:
186
303
  # $statsd.histogram('user.count', User.count)
187
- def histogram(stat, value, opts={})
304
+ def histogram(stat, value, opts=EMPTY_OPTIONS)
188
305
  send_stats stat, value, HISTOGRAM_TYPE, opts
189
306
  end
190
307
 
308
+ # Sends a value to be tracked as a distribution to the statsd server.
309
+ # Note: Distributions are a beta feature of Datadog and not generally
310
+ # available. Distributions must be specifically enabled for your
311
+ # organization.
312
+ #
313
+ # @param [String] stat stat name.
314
+ # @param [Numeric] value distribution value.
315
+ # @param [Hash] opts the options to create the metric with
316
+ # @option opts [Numeric] :sample_rate sample rate, 1 for always
317
+ # @option opts [Array<String>] :tags An array of tags
318
+ # @example Report the current user count:
319
+ # $statsd.distribution('user.count', User.count)
320
+ def distribution(stat, value, opts=EMPTY_OPTIONS)
321
+ send_stats stat, value, DISTRIBUTION_TYPE, opts
322
+ end
323
+
191
324
  # Sends a timing (in ms) for the given stat to the statsd server. The
192
325
  # sample_rate determines what percentage of the time this report is sent. The
193
326
  # statsd server then uses the sample_rate to correctly track the average
@@ -198,7 +331,7 @@ module Datadog
198
331
  # @param [Hash] opts the options to create the metric with
199
332
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
200
333
  # @option opts [Array<String>] :tags An array of tags
201
- def timing(stat, ms, opts={})
334
+ def timing(stat, ms, opts=EMPTY_OPTIONS)
202
335
  opts = {:sample_rate => opts} if opts.is_a? Numeric
203
336
  send_stats stat, ms, TIMING_TYPE, opts
204
337
  end
@@ -216,13 +349,15 @@ module Datadog
216
349
  # @see #timing
217
350
  # @example Report the time (in ms) taken to activate an account
218
351
  # $statsd.time('account.activate') { @account.activate! }
219
- def time(stat, opts={})
352
+ def time(stat, opts=EMPTY_OPTIONS)
220
353
  opts = {:sample_rate => opts} if opts.is_a? Numeric
221
- start = Time.now
354
+ start = (PROCESS_TIME_SUPPORTED ? Process.clock_gettime(Process::CLOCK_MONOTONIC) : Time.now.to_f)
222
355
  return yield
223
356
  ensure
224
- time_since(stat, start, opts)
357
+ finished = (PROCESS_TIME_SUPPORTED ? Process.clock_gettime(Process::CLOCK_MONOTONIC) : Time.now.to_f)
358
+ timing(stat, ((finished - start) * 1000).round, opts)
225
359
  end
360
+
226
361
  # Sends a value to be tracked as a set to the statsd server.
227
362
  #
228
363
  # @param [String] stat stat name.
@@ -232,12 +367,11 @@ module Datadog
232
367
  # @option opts [Array<String>] :tags An array of tags
233
368
  # @example Record a unique visitory by id:
234
369
  # $statsd.set('visitors.uniques', User.id)
235
- def set(stat, value, opts={})
370
+ def set(stat, value, opts=EMPTY_OPTIONS)
236
371
  opts = {:sample_rate => opts} if opts.is_a? Numeric
237
372
  send_stats stat, value, SET_TYPE, opts
238
373
  end
239
374
 
240
-
241
375
  # This method allows you to send custom service check statuses.
242
376
  #
243
377
  # @param [String] name Service check name
@@ -249,31 +383,8 @@ module Datadog
249
383
  # @option opts [String, nil] :message (nil) A message to associate with this service check status
250
384
  # @example Report a critical service check status
251
385
  # $statsd.service_check('my.service.check', Statsd::CRITICAL, :tags=>['urgent'])
252
- def service_check(name, status, opts={})
253
- service_check_string = format_service_check(name, status, opts)
254
- send_to_socket service_check_string
255
- end
256
-
257
- def format_service_check(name, status, opts={})
258
- sc_string = "_sc|#{name}|#{status}"
259
-
260
- SC_OPT_KEYS.each do |key, shorthand_key|
261
- next unless opts[key]
262
-
263
- if key == :tags
264
- tags = opts[:tags].map {|tag| escape_tag_content(tag) }
265
- tags = "#{tags.join(COMMA)}" unless tags.empty?
266
- sc_string << "|##{tags}"
267
- elsif key == :message
268
- message = remove_pipes(opts[:message])
269
- escaped_message = escape_service_check_message(message)
270
- sc_string << "|m:#{escaped_message}"
271
- else
272
- value = remove_pipes(opts[key])
273
- sc_string << "|#{shorthand_key}#{value}"
274
- end
275
- end
276
- return sc_string
386
+ def service_check(name, status, opts=EMPTY_OPTIONS)
387
+ send_stat format_service_check(name, status, opts)
277
388
  end
278
389
 
279
390
  # This end point allows you to post events to the stream. You can tag them, set priority and even aggregate them with other events.
@@ -294,11 +405,8 @@ module Datadog
294
405
  # @option opts [Array<String>] :tags tags to be added to every metric
295
406
  # @example Report an awful event:
296
407
  # $statsd.event('Something terrible happened', 'The end is near if we do nothing', :alert_type=>'warning', :tags=>['end_of_times','urgent'])
297
- def event(title, text, opts={})
298
- event_string = format_event(title, text, opts)
299
- raise "Event #{title} payload is too big (more that 8KB), event discarded" if event_string.length > 8 * 1024
300
-
301
- send_to_socket event_string
408
+ def event(title, text, opts=EMPTY_OPTIONS)
409
+ send_stat format_event(title, text, opts)
302
410
  end
303
411
 
304
412
  # Send several metrics in the same UDP Packet
@@ -309,18 +417,56 @@ module Datadog
309
417
  # s.gauge('users.online',156)
310
418
  # s.increment('page.views')
311
419
  # end
312
- def batch()
313
- @batch_nesting_depth += 1
314
- yield self
315
- ensure
316
- @batch_nesting_depth -= 1
317
- flush_buffer if @batch_nesting_depth == 0
420
+ def batch
421
+ @batch.open { yield self }
422
+ end
423
+
424
+ # Close the underlying socket
425
+ def close
426
+ @connection.close
318
427
  end
319
428
 
320
- def format_event(title, text, opts={})
429
+ private
430
+
431
+ NEW_LINE = "\n".freeze
432
+ ESC_NEW_LINE = "\\n".freeze
433
+ COMMA = ",".freeze
434
+ PIPE = "|".freeze
435
+ DOT = ".".freeze
436
+ DOUBLE_COLON = "::".freeze
437
+ UNDERSCORE = "_".freeze
438
+ PROCESS_TIME_SUPPORTED = (RUBY_VERSION >= "2.1.0")
439
+ EMPTY_OPTIONS = {}.freeze
440
+
441
+ private_constant :NEW_LINE, :ESC_NEW_LINE, :COMMA, :PIPE, :DOT,
442
+ :DOUBLE_COLON, :UNDERSCORE, :EMPTY_OPTIONS
443
+
444
+ def format_service_check(name, status, opts=EMPTY_OPTIONS)
445
+ sc_string = "_sc|#{name}|#{status}".dup
446
+
447
+ SC_OPT_KEYS.each do |key, shorthand_key|
448
+ next unless opts[key]
449
+
450
+ if key == :tags
451
+ if tags_string = tags_as_string(opts)
452
+ sc_string << "|##{tags_string}"
453
+ end
454
+ elsif key == :message
455
+ message = remove_pipes(opts[:message])
456
+ escaped_message = escape_service_check_message(message)
457
+ sc_string << "|m:#{escaped_message}"
458
+ else
459
+ value = remove_pipes(opts[key])
460
+ sc_string << "|#{shorthand_key}#{value}"
461
+ end
462
+ end
463
+ sc_string
464
+ end
465
+
466
+ def format_event(title, text, opts=EMPTY_OPTIONS)
321
467
  escaped_title = escape_event_content(title)
322
468
  escaped_text = escape_event_content(text)
323
- event_string_data = "_e{#{escaped_title.length},#{escaped_text.length}}:#{escaped_title}|#{escaped_text}"
469
+ event_string_data = "_e{#{escaped_title.length},#{escaped_text.length}}:#{escaped_title}|#{escaped_text}".dup
324
470
 
325
471
  # We construct the string to be sent by adding '|key:value' parts to it when needed
326
472
  # All pipes ('|') in the metadata are removed. Title and Text can keep theirs
@@ -332,72 +478,46 @@ module Datadog
332
478
  end
333
479
 
334
480
  # Tags are joined and added as last part to the string to be sent
335
- full_tags = (tags + (opts[:tags] || [])).map {|tag| escape_tag_content(tag) }
336
- unless full_tags.empty?
337
- event_string_data << "|##{full_tags.join(COMMA)}"
481
+ if tags_string = tags_as_string(opts)
482
+ event_string_data << "|##{tags_string}"
338
483
  end
339
484
 
340
- raise "Event #{title} payload is too big (more that 8KB), event discarded" if event_string_data.length > 8192 # 8 * 1024 = 8192
341
- return event_string_data
485
+ raise "Event #{title} payload is too big (more that 8KB), event discarded" if event_string_data.length > MAX_EVENT_SIZE
486
+ event_string_data
342
487
  end
343
488
 
344
- # Close the underlying socket
345
- def close()
346
- @socket.close
489
+ def tags_as_string(opts)
490
+ if tag_arr = opts[:tags]
491
+ tag_arr = tag_arr.map { |tag| escape_tag_content(tag) }
492
+ tag_arr = tags + tag_arr # @tags are normalized when set, so not need to normalize them again
493
+ else
494
+ tag_arr = tags
495
+ end
496
+ tag_arr.join(COMMA) unless tag_arr.empty?
347
497
  end
348
498
 
349
- private
350
-
351
- NEW_LINE = "\n".freeze
352
- ESC_NEW_LINE = "\\n".freeze
353
- COMMA = ",".freeze
354
- BLANK = "".freeze
355
- PIPE = "|".freeze
356
- DOT = ".".freeze
357
- DOUBLE_COLON = "::".freeze
358
- UNDERSCORE = "_".freeze
359
-
360
- private_constant :NEW_LINE, :ESC_NEW_LINE, :COMMA, :BLANK, :PIPE, :DOT,
361
- :DOUBLE_COLON, :UNDERSCORE
362
-
363
499
  def escape_event_content(msg)
364
500
  msg.gsub NEW_LINE, ESC_NEW_LINE
365
501
  end
366
502
 
367
503
  def escape_tag_content(tag)
368
- remove_pipes(tag.to_s).gsub COMMA, BLANK
369
- end
370
-
371
- def escape_tag_content!(tag)
372
- tag = tag.to_s
373
- tag.gsub!(PIPE, BLANK)
374
- tag.gsub!(COMMA, BLANK)
504
+ tag = remove_pipes(tag.to_s)
505
+ tag.delete! COMMA
375
506
  tag
376
507
  end
377
508
 
378
509
  def remove_pipes(msg)
379
- msg.gsub PIPE, BLANK
510
+ msg.delete PIPE
380
511
  end
381
512
 
382
513
  def escape_service_check_message(msg)
383
514
  escape_event_content(msg).gsub('m:'.freeze, 'm\:'.freeze)
384
515
  end
385
516
 
386
- def time_since(stat, start, opts)
387
- timing(stat, ((Time.now - start) * 1000).round, opts)
388
- end
389
-
390
- def join_array_to_str(str, array, joiner)
391
- array.each_with_index do |item, i|
392
- str << joiner unless i == 0
393
- str << item
394
- end
395
- end
396
-
397
- def send_stats(stat, delta, type, opts={})
517
+ def send_stats(stat, delta, type, opts=EMPTY_OPTIONS)
398
518
  sample_rate = opts[:sample_rate] || 1
399
519
  if sample_rate == 1 or rand < sample_rate
400
- full_stat = ''
520
+ full_stat = ''.dup
401
521
  full_stat << @prefix if @prefix
402
522
 
403
523
  stat = stat.is_a?(String) ? stat.dup : stat.to_s
@@ -417,14 +537,10 @@ module Datadog
417
537
  full_stat << sample_rate.to_s
418
538
  end
419
539
 
420
-
421
- tag_arr = opts[:tags].to_a
422
- tag_arr.map! { |tag| t = tag.to_s.dup; escape_tag_content!(t); t }
423
- ts = tags.to_a + tag_arr
424
- unless ts.empty?
540
+ if tags_string = tags_as_string(opts)
425
541
  full_stat << PIPE
426
542
  full_stat << '#'.freeze
427
- join_array_to_str(full_stat, ts, COMMA)
543
+ full_stat << tags_string
428
544
  end
429
545
 
430
546
  send_stat(full_stat)
@@ -432,62 +548,11 @@ module Datadog
432
548
  end
433
549
 
434
550
  def send_stat(message)
435
- if @batch_nesting_depth > 0
436
- @buffer << message
437
- flush_buffer if @buffer.length >= @max_buffer_size
438
- else
439
- send_to_socket(message)
440
- end
441
- end
442
-
443
- def flush_buffer
444
- return @buffer if @buffer.empty?
445
- send_to_socket(@buffer.join(NEW_LINE))
446
- @buffer = Array.new
447
- end
448
-
449
- def connect_to_socket
450
- if !@socket_path.nil?
451
- socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM)
452
- socket.connect(Socket.pack_sockaddr_un(@socket_path))
551
+ if @batch.open?
552
+ @batch.add message
453
553
  else
454
- socket = UDPSocket.new
455
- socket.connect(@host, @port)
554
+ @connection.write(message)
456
555
  end
457
- socket
458
- end
459
-
460
- def sock
461
- @socket ||= connect_to_socket
462
- end
463
-
464
- def send_to_socket(message)
465
- self.class.logger.debug { "Statsd: #{message}" } if self.class.logger
466
- if @socket_path.nil?
467
- sock.send(message, 0)
468
- else
469
- sock.sendmsg_nonblock(message)
470
- end
471
- rescue => boom
472
- if @socket_path && (boom.is_a?(Errno::ECONNREFUSED) ||
473
- boom.is_a?(Errno::ECONNRESET) ||
474
- boom.is_a?(Errno::ENOENT))
475
- return @socket = nil
476
- end
477
- # Try once to reconnect if the socket has been closed
478
- retries ||= 1
479
- if retries <= 1 && boom.is_a?(IOError) && boom.message =~ /closed stream/i
480
- retries += 1
481
- begin
482
- @socket = connect_to_socket
483
- retry
484
- rescue => e
485
- boom = e
486
- end
487
- end
488
-
489
- self.class.logger.error { "Statsd: #{boom.class} #{boom}" } if self.class.logger
490
- nil
491
556
  end
492
557
  end
493
558
  end
metadata CHANGED
@@ -1,71 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dogstatsd-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 4.0.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: 2017-12-21 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: minitest
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: yard
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: 0.6.0
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: 0.6.0
41
- - !ruby/object:Gem::Dependency
42
- name: jeweler
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '1.8'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '1.8'
55
- - !ruby/object:Gem::Dependency
56
- name: simplecov
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
11
+ date: 2018-08-20 00:00:00.000000000 Z
12
+ dependencies: []
69
13
  description: A Ruby DogStastd client
70
14
  email: code@datadoghq.com
71
15
  executables: []
@@ -89,7 +33,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
89
33
  requirements:
90
34
  - - ">="
91
35
  - !ruby/object:Gem::Version
92
- version: '0'
36
+ version: 2.0.0
93
37
  required_rubygems_version: !ruby/object:Gem::Requirement
94
38
  requirements:
95
39
  - - ">="
@@ -97,7 +41,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
97
41
  version: '0'
98
42
  requirements: []
99
43
  rubyforge_project:
100
- rubygems_version: 2.7.1
44
+ rubygems_version: 2.7.7
101
45
  signing_key:
102
46
  specification_version: 4
103
47
  summary: A Ruby DogStatsd client