dogstatsd-ruby 3.0.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. checksums.yaml +5 -5
  2. data/lib/datadog/statsd.rb +85 -53
  3. metadata +4 -61
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 58a9207adf16ba8265e91fd56b6315b35ecc9b58
4
- data.tar.gz: 9fc64d84a4b0277752bad6a5b523f1ab1ac989c2
2
+ SHA256:
3
+ metadata.gz: 046a0af9f230451c91fdeb9ae128c550b1c20623fa017d326e63a2f053f7cc9a
4
+ data.tar.gz: e2ba838497de521e89d386645a0228f819fdd03c28acc05a4789aa7f8d979de8
5
5
  SHA512:
6
- metadata.gz: 89574d06e8d66f00a203d6d84b9e20f70b1d64e90fd2dfa644bdab8993a0420065b2e426d574fc0c0239fc69f49a65b0d215359c50ec2fd00dd9b9214f8c9f13
7
- data.tar.gz: d97dc13266fe51bc7d49ebb8e56235aef70a6773e89d890ffe4774e0406489f6a7fadb43818f1000fc70f7520d6baec19d05b395c732cce9ea22ef9107597de9
6
+ metadata.gz: f2048376477bd1af31a63b75d17638df999c9f6341f967b2c826177add2a13831a54a72729e2a5c62aa09fa4aca6dccbaba308c79d9adcd470808d3bb80cde59
7
+ data.tar.gz: 2e0ac5fc90f8a2359f35a754034bb6474c1169ac8c8f3301257555553e1aca4f708cecd4d9245ff835cbe4396561a89d3bd3700ef6cea6c92c343a1e41ca2055
@@ -49,8 +49,10 @@ module Datadog
49
49
  COUNTER_TYPE = 'c'.freeze
50
50
  GAUGE_TYPE = 'g'.freeze
51
51
  HISTOGRAM_TYPE = 'h'.freeze
52
+ DISTRIBUTION_TYPE = 'd'.freeze
52
53
  TIMING_TYPE = 'ms'.freeze
53
54
  SET_TYPE = 's'.freeze
55
+ VERSION = "3.3.0".freeze
54
56
 
55
57
  # A namespace to prepend to all statsd calls. Defaults to no namespace.
56
58
  attr_reader :namespace
@@ -61,13 +63,12 @@ module Datadog
61
63
  # StatsD port. Defaults to 8125.
62
64
  attr_reader :port
63
65
 
66
+ # DogStatsd unix socket path. Not used by default.
67
+ attr_reader :socket_path
68
+
64
69
  # Global tags to be added to every statsd call. Defaults to no tags.
65
70
  attr_reader :tags
66
71
 
67
- # True if we should batch up data before sending it, or false if we
68
- # want to send data immediately.
69
- attr_reader :should_batch
70
-
71
72
  # Buffer containing the statsd message before they are sent in batch
72
73
  attr_reader :buffer
73
74
 
@@ -80,8 +81,9 @@ module Datadog
80
81
  end
81
82
 
82
83
  # Return the current version of the library.
84
+ # deprecated, but cannot be removed since uses might use it to check the version against older releases
83
85
  def self.VERSION
84
- "3.0.0"
86
+ VERSION
85
87
  end
86
88
 
87
89
  # @param [String] host your statsd host
@@ -90,13 +92,14 @@ module Datadog
90
92
  # @option opts [Array<String>] :tags tags to be added to every metric
91
93
  def initialize(host = DEFAULT_HOST, port = DEFAULT_PORT, opts = {}, max_buffer_size=50)
92
94
  self.host, self.port = host, port
95
+ @socket_path = opts[:socket_path]
93
96
  @prefix = nil
94
- @socket = connect_to_socket(host, port)
97
+ @socket = connect_to_socket if @socket_path.nil?
95
98
  self.namespace = opts[:namespace]
96
99
  self.tags = opts[:tags]
97
100
  @buffer = Array.new
98
101
  self.max_buffer_size = max_buffer_size
99
- @should_batch = false
102
+ @batch_nesting_depth = 0
100
103
  end
101
104
 
102
105
  def namespace=(namespace) #:nodoc:
@@ -114,7 +117,7 @@ module Datadog
114
117
 
115
118
  def tags=(tags) #:nodoc:
116
119
  raise ArgumentError, 'tags must be a Array<String>' unless tags.nil? or tags.is_a? Array
117
- @tags = (tags || []).map {|tag| escape_tag_content(tag)}
120
+ @tags = (tags || []).compact.map! {|tag| escape_tag_content(tag)}
118
121
  end
119
122
 
120
123
  # Sends an increment (count = 1) for the given stat to the statsd server.
@@ -126,6 +129,7 @@ module Datadog
126
129
  # @option opts [Numeric] :by increment value, default 1
127
130
  # @see #count
128
131
  def increment(stat, opts={})
132
+ opts = {:sample_rate => opts} if opts.is_a? Numeric
129
133
  incr_value = opts.fetch(:by, 1)
130
134
  count stat, incr_value, opts
131
135
  end
@@ -139,6 +143,7 @@ module Datadog
139
143
  # @option opts [Numeric] :by decrement value, default 1
140
144
  # @see #count
141
145
  def decrement(stat, opts={})
146
+ opts = {:sample_rate => opts} if opts.is_a? Numeric
142
147
  decr_value = - opts.fetch(:by, 1)
143
148
  count stat, decr_value, opts
144
149
  end
@@ -151,6 +156,7 @@ module Datadog
151
156
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
152
157
  # @option opts [Array<String>] :tags An array of tags
153
158
  def count(stat, count, opts={})
159
+ opts = {:sample_rate => opts} if opts.is_a? Numeric
154
160
  send_stats stat, count, COUNTER_TYPE, opts
155
161
  end
156
162
 
@@ -168,6 +174,7 @@ module Datadog
168
174
  # @example Report the current user count:
169
175
  # $statsd.gauge('user.count', User.count)
170
176
  def gauge(stat, value, opts={})
177
+ opts = {:sample_rate => opts} if opts.is_a? Numeric
171
178
  send_stats stat, value, GAUGE_TYPE, opts
172
179
  end
173
180
 
@@ -184,6 +191,22 @@ module Datadog
184
191
  send_stats stat, value, HISTOGRAM_TYPE, opts
185
192
  end
186
193
 
194
+ # Sends a value to be tracked as a distribution to the statsd server.
195
+ # Note: Distributions are a beta feature of Datadog and not generally
196
+ # available. Distributions must be specifically enabled for your
197
+ # organization.
198
+ #
199
+ # @param [String] stat stat name.
200
+ # @param [Numeric] value distribution value.
201
+ # @param [Hash] opts the options to create the metric with
202
+ # @option opts [Numeric] :sample_rate sample rate, 1 for always
203
+ # @option opts [Array<String>] :tags An array of tags
204
+ # @example Report the current user count:
205
+ # $statsd.distribution('user.count', User.count)
206
+ def distribution(stat, value, opts={})
207
+ send_stats stat, value, DISTRIBUTION_TYPE, opts
208
+ end
209
+
187
210
  # Sends a timing (in ms) for the given stat to the statsd server. The
188
211
  # sample_rate determines what percentage of the time this report is sent. The
189
212
  # statsd server then uses the sample_rate to correctly track the average
@@ -195,6 +218,7 @@ module Datadog
195
218
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
196
219
  # @option opts [Array<String>] :tags An array of tags
197
220
  def timing(stat, ms, opts={})
221
+ opts = {:sample_rate => opts} if opts.is_a? Numeric
198
222
  send_stats stat, ms, TIMING_TYPE, opts
199
223
  end
200
224
 
@@ -212,10 +236,12 @@ module Datadog
212
236
  # @example Report the time (in ms) taken to activate an account
213
237
  # $statsd.time('account.activate') { @account.activate! }
214
238
  def time(stat, opts={})
215
- start = Time.now
239
+ opts = {:sample_rate => opts} if opts.is_a? Numeric
240
+ start = (PROCESS_TIME_SUPPORTED ? Process.clock_gettime(Process::CLOCK_MONOTONIC) : Time.now.to_f)
216
241
  return yield
217
242
  ensure
218
- time_since(stat, start, opts)
243
+ finished = (PROCESS_TIME_SUPPORTED ? Process.clock_gettime(Process::CLOCK_MONOTONIC) : Time.now.to_f)
244
+ timing(stat, ((finished - start) * 1000).round, opts)
219
245
  end
220
246
  # Sends a value to be tracked as a set to the statsd server.
221
247
  #
@@ -227,10 +253,10 @@ module Datadog
227
253
  # @example Record a unique visitory by id:
228
254
  # $statsd.set('visitors.uniques', User.id)
229
255
  def set(stat, value, opts={})
256
+ opts = {:sample_rate => opts} if opts.is_a? Numeric
230
257
  send_stats stat, value, SET_TYPE, opts
231
258
  end
232
259
 
233
-
234
260
  # This method allows you to send custom service check statuses.
235
261
  #
236
262
  # @param [String] name Service check name
@@ -254,9 +280,9 @@ module Datadog
254
280
  next unless opts[key]
255
281
 
256
282
  if key == :tags
257
- tags = opts[:tags].map {|tag| escape_tag_content(tag) }
258
- tags = "#{tags.join(COMMA)}" unless tags.empty?
259
- sc_string << "|##{tags}"
283
+ if tags_string = tags_as_string(opts)
284
+ sc_string << "|##{tags_string}"
285
+ end
260
286
  elsif key == :message
261
287
  message = remove_pipes(opts[:message])
262
288
  escaped_message = escape_service_check_message(message)
@@ -303,11 +329,11 @@ module Datadog
303
329
  # s.increment('page.views')
304
330
  # end
305
331
  def batch()
306
- @should_batch = true
332
+ @batch_nesting_depth += 1
307
333
  yield self
308
- flush_buffer
309
334
  ensure
310
- @should_batch = false
335
+ @batch_nesting_depth -= 1
336
+ flush_buffer if @batch_nesting_depth == 0
311
337
  end
312
338
 
313
339
  def format_event(title, text, opts={})
@@ -325,9 +351,8 @@ module Datadog
325
351
  end
326
352
 
327
353
  # Tags are joined and added as last part to the string to be sent
328
- full_tags = (tags + (opts[:tags] || [])).map {|tag| escape_tag_content(tag) }
329
- unless full_tags.empty?
330
- event_string_data << "|##{full_tags.join(COMMA)}"
354
+ if tags_string = tags_as_string(opts)
355
+ event_string_data << "|##{tags_string}"
331
356
  end
332
357
 
333
358
  raise "Event #{title} payload is too big (more that 8KB), event discarded" if event_string_data.length > 8192 # 8 * 1024 = 8192
@@ -344,48 +369,40 @@ module Datadog
344
369
  NEW_LINE = "\n".freeze
345
370
  ESC_NEW_LINE = "\\n".freeze
346
371
  COMMA = ",".freeze
347
- BLANK = "".freeze
348
372
  PIPE = "|".freeze
349
373
  DOT = ".".freeze
350
374
  DOUBLE_COLON = "::".freeze
351
375
  UNDERSCORE = "_".freeze
376
+ PROCESS_TIME_SUPPORTED = (RUBY_VERSION >= "2.1.0")
352
377
 
353
- private_constant :NEW_LINE, :ESC_NEW_LINE, :COMMA, :BLANK, :PIPE, :DOT,
378
+ private_constant :NEW_LINE, :ESC_NEW_LINE, :COMMA, :PIPE, :DOT,
354
379
  :DOUBLE_COLON, :UNDERSCORE
355
380
 
381
+ def tags_as_string(opts)
382
+ tag_arr = opts[:tags] || []
383
+ tag_arr = tag_arr.map { |tag| escape_tag_content(tag) }
384
+ tag_arr = tags + tag_arr # @tags are normalized when set, so not need to normalize them again
385
+ tag_arr.join(COMMA) unless tag_arr.empty?
386
+ end
387
+
356
388
  def escape_event_content(msg)
357
389
  msg.gsub NEW_LINE, ESC_NEW_LINE
358
390
  end
359
391
 
360
392
  def escape_tag_content(tag)
361
- remove_pipes(tag).gsub COMMA, BLANK
362
- end
363
-
364
- def escape_tag_content!(tag)
365
- tag.gsub!(PIPE, BLANK)
366
- tag.gsub!(COMMA, BLANK)
393
+ tag = remove_pipes(tag.to_s)
394
+ tag.delete! COMMA
367
395
  tag
368
396
  end
369
397
 
370
398
  def remove_pipes(msg)
371
- msg.gsub PIPE, BLANK
399
+ msg.delete PIPE
372
400
  end
373
401
 
374
402
  def escape_service_check_message(msg)
375
403
  escape_event_content(msg).gsub('m:'.freeze, 'm\:'.freeze)
376
404
  end
377
405
 
378
- def time_since(stat, start, opts)
379
- timing(stat, ((Time.now - start) * 1000).round, opts)
380
- end
381
-
382
- def join_array_to_str(str, array, joiner)
383
- array.each_with_index do |item, i|
384
- str << joiner unless i == 0
385
- str << item
386
- end
387
- end
388
-
389
406
  def send_stats(stat, delta, type, opts={})
390
407
  sample_rate = opts[:sample_rate] || 1
391
408
  if sample_rate == 1 or rand < sample_rate
@@ -409,14 +426,10 @@ module Datadog
409
426
  full_stat << sample_rate.to_s
410
427
  end
411
428
 
412
-
413
- tag_arr = opts[:tags].to_a
414
- tag_arr.map! { |tag| t = tag.dup; escape_tag_content!(t); t }
415
- ts = tags.to_a + tag_arr
416
- unless ts.empty?
429
+ if tags_string = tags_as_string(opts)
417
430
  full_stat << PIPE
418
431
  full_stat << '#'.freeze
419
- join_array_to_str(full_stat, ts, COMMA)
432
+ full_stat << tags_string
420
433
  end
421
434
 
422
435
  send_stat(full_stat)
@@ -424,7 +437,7 @@ module Datadog
424
437
  end
425
438
 
426
439
  def send_stat(message)
427
- if @should_batch
440
+ if @batch_nesting_depth > 0
428
441
  @buffer << message
429
442
  flush_buffer if @buffer.length >= @max_buffer_size
430
443
  else
@@ -432,27 +445,46 @@ module Datadog
432
445
  end
433
446
  end
434
447
 
435
- def flush_buffer()
448
+ def flush_buffer
449
+ return @buffer if @buffer.empty?
436
450
  send_to_socket(@buffer.join(NEW_LINE))
437
451
  @buffer = Array.new
438
452
  end
439
453
 
440
- def connect_to_socket(host, port)
441
- socket = UDPSocket.new
442
- socket.connect(host, port)
454
+ def connect_to_socket
455
+ if !@socket_path.nil?
456
+ socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM)
457
+ socket.connect(Socket.pack_sockaddr_un(@socket_path))
458
+ else
459
+ socket = UDPSocket.new
460
+ socket.connect(@host, @port)
461
+ end
443
462
  socket
444
463
  end
445
464
 
465
+ def sock
466
+ @socket ||= connect_to_socket
467
+ end
468
+
446
469
  def send_to_socket(message)
447
470
  self.class.logger.debug { "Statsd: #{message}" } if self.class.logger
448
- @socket.send(message, 0)
471
+ if @socket_path.nil?
472
+ sock.send(message, 0)
473
+ else
474
+ sock.sendmsg_nonblock(message)
475
+ end
449
476
  rescue => boom
477
+ if @socket_path && (boom.is_a?(Errno::ECONNREFUSED) ||
478
+ boom.is_a?(Errno::ECONNRESET) ||
479
+ boom.is_a?(Errno::ENOENT))
480
+ return @socket = nil
481
+ end
450
482
  # Try once to reconnect if the socket has been closed
451
483
  retries ||= 1
452
484
  if retries <= 1 && boom.is_a?(IOError) && boom.message =~ /closed stream/i
453
485
  retries += 1
454
486
  begin
455
- @socket = connect_to_socket(host, port)
487
+ @socket = connect_to_socket
456
488
  retry
457
489
  rescue => e
458
490
  boom = e
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.0.0
4
+ version: 3.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: 2017-05-18 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-02-04 00:00:00.000000000 Z
12
+ dependencies: []
69
13
  description: A Ruby DogStastd client
70
14
  email: code@datadoghq.com
71
15
  executables: []
@@ -97,9 +41,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
97
41
  version: '0'
98
42
  requirements: []
99
43
  rubyforge_project:
100
- rubygems_version: 2.6.8
44
+ rubygems_version: 2.7.1
101
45
  signing_key:
102
46
  specification_version: 4
103
47
  summary: A Ruby DogStatsd client
104
48
  test_files: []
105
- has_rdoc: