dogstatsd-ruby 3.2.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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