dogstatsd-ruby 3.0.0 → 3.3.0

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