dogstatsd-ruby 4.2.0 → 4.4.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 +28 -4
  3. data/lib/datadog/statsd.rb +91 -49
  4. metadata +4 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c8e8d0982550cf2f3e3dddee6eee8dcf88534ccf4e26ef47731c2866b85e81f2
4
- data.tar.gz: 8f9d33a02a6b86e343144c98511e243a65bdaeb2f881ae00cd7842528158f3f6
3
+ metadata.gz: 25baf4b0dc57e8ed9a79bd2115224f22de9097200ccf4931b54fbb1e40905363
4
+ data.tar.gz: a51c91dc366316b6edbadc64eb5e7383b6b100c9a2a859eb9bc31b9bef9ec7e2
5
5
  SHA512:
6
- metadata.gz: 68e6cc9c4bbbbffa546eaefd643785ca945ae00dfd03d84d5984262d0061f25b059c325063220fd15bdd354d6ec721ba7d50ad7b7c2310d2b64c24b49314ba36
7
- data.tar.gz: c070c0fd60a513b3c89a8eff5c4c755e38b5d646529840adca886a52651831f7bef1bdf437723329237f1faf4066960bee32ae5e5b6b9f4ddeb1eb69bfb87afe
6
+ metadata.gz: 225ef009dcb475534ec746d9eaa6e8b156ad7b50291c2894ba9171806bc91231150510a65a47ec754dbf519f80fe7381c589d34f35e983519fb8e991347e1a12
7
+ data.tar.gz: e8609033506332625db4a6655adceacfb7fd1b85346020c1582dd032815ea56158d46442cecafd40fdd7bf098745e41ab1e70323b323ff63dd8bb43be3be8b88
data/README.md CHANGED
@@ -28,9 +28,10 @@ statsd = Datadog::Statsd.new('localhost', 8125)
28
28
 
29
29
  # Increment a counter.
30
30
  statsd.increment('page.views')
31
+ statsd.increment('messages.count', by: 2, tags: ['kind:incoming'])
31
32
 
32
33
  # Record a gauge 50% of the time.
33
- statsd.gauge('users.online', 123, :sample_rate=>0.5)
34
+ statsd.gauge('users.online', 123, sample_rate: 0.5)
34
35
 
35
36
  # Sample a histogram
36
37
  statsd.histogram('file.upload.size', 1234)
@@ -48,7 +49,10 @@ statsd.batch do |s|
48
49
  end
49
50
 
50
51
  # Tag a metric.
51
- statsd.histogram('query.time', 10, :tags => ["version:1"])
52
+ statsd.histogram('query.time', 10, tags: ['version:1'])
53
+
54
+ # Tag a metric by passing in a Hash
55
+ statsd.histogram('query.time', 10, :tags => {version: 1})
52
56
 
53
57
  # Auto-close socket after end of block
54
58
  Datadog::Statsd.open('localhost', 8125) do |s|
@@ -62,14 +66,34 @@ Aggregation in the stream is made on hostname/event_type/source_type/aggregation
62
66
 
63
67
  ``` ruby
64
68
  # Post a simple message
65
- statsd.event("There might be a storm tomorrow", "A friend warned me earlier.")
69
+ statsd.event('There might be a storm tomorrow', 'A friend warned me earlier.')
66
70
 
67
71
  # Cry for help
68
- statsd.event("SO MUCH SNOW", "Started yesterday and it won't stop !!", :alert_type => "error", :tags => ["urgent", "endoftheworld"])
72
+ statsd.event(
73
+ 'SO MUCH SNOW',
74
+ "Started yesterday and it won't stop !!",
75
+ alert_type: 'error',
76
+ tags: ['urgent', 'endoftheworld']
77
+ )
69
78
  ```
70
79
 
71
80
 
72
81
 
82
+ Origin detection over UDP
83
+ -------------
84
+ Origin detection is a method to detect which pod DogStatsD packets are coming from in order to add the pod's tags to the tag list.
85
+
86
+ To enable origin detection over UDP, add the following lines to your application manifest
87
+ ```yaml
88
+ env:
89
+ - name: DD_ENTITY_ID
90
+ valueFrom:
91
+ fieldRef:
92
+ fieldPath: metadata.uid
93
+ ```
94
+ The DogStatsD client attaches an internal tag, `entity_id`. The value of this tag is the content of the `DD_ENTITY_ID` environment variable, which is the pod’s UID.
95
+
96
+
73
97
  Documentation
74
98
  -------------
75
99
 
@@ -33,32 +33,15 @@ module Datadog
33
33
  # DogStatsd unix socket path. Not used by default.
34
34
  attr_reader :socket_path
35
35
 
36
- def initialize(host, port, socket_path, logger)
37
- @host = host || ENV.fetch('DD_AGENT_HOST', nil) || DEFAULT_HOST
38
- @port = port || ENV.fetch('DD_DOGSTATSD_PORT', nil) || DEFAULT_PORT
39
- @socket_path = socket_path
40
- @logger = logger
36
+ # Close the underlying socket
37
+ def close
38
+ @socket && @socket.close
41
39
  end
42
40
 
43
41
  def write(message)
44
42
  @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
43
+ send_message(message)
50
44
  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
45
  # Try once to reconnect if the socket has been closed
63
46
  retries ||= 1
64
47
  if retries <= 1 && boom.is_a?(Errno::ENOTCONN) or
@@ -76,27 +59,55 @@ module Datadog
76
59
  nil
77
60
  end
78
61
 
79
- # Close the underlying socket
80
- def close
81
- @socket && @socket.close
82
- end
83
-
84
62
  private
85
63
 
86
64
  def socket
87
65
  @socket ||= connect
88
66
  end
67
+ end
68
+
69
+ class UDPConnection < Connection
70
+ def initialize(host, port, logger)
71
+ @host = host || ENV.fetch('DD_AGENT_HOST', nil) || DEFAULT_HOST
72
+ @port = port || ENV.fetch('DD_DOGSTATSD_PORT', nil) || DEFAULT_PORT
73
+ @logger = logger
74
+ end
75
+
76
+ private
89
77
 
90
78
  def connect
91
- if @socket_path.nil?
92
- socket = UDPSocket.new
93
- socket.connect(@host, @port)
94
- else
95
- socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM)
96
- socket.connect(Socket.pack_sockaddr_un(@socket_path))
97
- end
79
+ socket = UDPSocket.new
80
+ socket.connect(@host, @port)
81
+ socket
82
+ end
83
+
84
+ def send_message(message)
85
+ socket.send(message, 0)
86
+ end
87
+ end
88
+
89
+ class UDSConnection < Connection
90
+ class BadSocketError < StandardError; end
91
+
92
+ def initialize(socket_path, logger)
93
+ @socket_path = socket_path
94
+ @logger = logger
95
+ end
96
+
97
+ private
98
+
99
+ def connect
100
+ socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM)
101
+ socket.connect(Socket.pack_sockaddr_un(@socket_path))
98
102
  socket
99
103
  end
104
+
105
+ def send_message(message)
106
+ socket.sendmsg_nonblock(message)
107
+ rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ENOENT => e
108
+ @socket = nil
109
+ raise BadSocketError, "#{e.class}: #{e}"
110
+ end
100
111
  end
101
112
 
102
113
  class Batch
@@ -181,7 +192,7 @@ module Datadog
181
192
  DISTRIBUTION_TYPE = 'd'.freeze
182
193
  TIMING_TYPE = 'ms'.freeze
183
194
  SET_TYPE = 's'.freeze
184
- VERSION = "4.2.0".freeze
195
+ VERSION = "4.4.0".freeze
185
196
 
186
197
  # A namespace to prepend to all statsd calls. Defaults to no namespace.
187
198
  attr_reader :namespace
@@ -195,16 +206,20 @@ module Datadog
195
206
  # Maximum buffer size in bytes before it is flushed
196
207
  attr_reader :max_buffer_bytes
197
208
 
209
+ # Default sample rate
210
+ attr_reader :sample_rate
211
+
198
212
  # Connection
199
213
  attr_reader :connection
200
214
 
201
215
  # @param [String] host your statsd host
202
216
  # @param [Integer] port your statsd port
203
217
  # @option [String] namespace set a namespace to be prepended to every metric name
204
- # @option [Array<String>] tags tags to be added to every metric
205
- # @option [Loger] logger for debugging
218
+ # @option [Array<String>|Hash] tags tags to be added to every metric
219
+ # @option [Logger] logger for debugging
206
220
  # @option [Integer] max_buffer_bytes max bytes to buffer when using #batch
207
221
  # @option [String] socket_path unix socket path
222
+ # @option [Float] default sample rate if not overridden
208
223
  def initialize(
209
224
  host = nil,
210
225
  port = nil,
@@ -212,15 +227,26 @@ module Datadog
212
227
  tags: nil,
213
228
  max_buffer_bytes: 8192,
214
229
  socket_path: nil,
215
- logger: nil
230
+ logger: nil,
231
+ sample_rate: nil
216
232
  )
217
- @connection = Connection.new(host, port, socket_path, logger)
233
+ if socket_path.nil?
234
+ @connection = UDPConnection.new(host, port, logger)
235
+ else
236
+ @connection = UDSConnection.new(socket_path, logger)
237
+ end
218
238
  @logger = logger
219
239
 
220
240
  @namespace = namespace
221
241
  @prefix = @namespace ? "#{@namespace}.".freeze : nil
222
242
 
223
- raise ArgumentError, 'tags must be a Array<String>' unless tags.nil? or tags.is_a? Array
243
+ @sample_rate = sample_rate
244
+
245
+ unless tags.nil? or tags.is_a? Array or tags.is_a? Hash
246
+ raise ArgumentError, 'tags must be a Array<String> or a Hash'
247
+ end
248
+
249
+ tags = tag_hash_to_array(tags) if tags.is_a? Hash
224
250
  @tags = (tags || []).compact.map! {|tag| escape_tag_content(tag)}
225
251
 
226
252
  # append the entity id to tags if DD_ENTITY_ID env var is not nil
@@ -381,8 +407,8 @@ module Datadog
381
407
  # @param [String] name Service check name
382
408
  # @param [String] status Service check status.
383
409
  # @param [Hash] opts the additional data about the service check
384
- # @option opts [Integer, nil] :timestamp (nil) Assign a timestamp to the event. Default is now when none
385
- # @option opts [String, nil] :hostname (nil) Assign a hostname to the event.
410
+ # @option opts [Integer, String, nil] :timestamp (nil) Assign a timestamp to the service check. Default is now when none
411
+ # @option opts [String, nil] :hostname (nil) Assign a hostname to the service check.
386
412
  # @option opts [Array<String>, nil] :tags (nil) An array of tags
387
413
  # @option opts [String, nil] :message (nil) A message to associate with this service check status
388
414
  # @example Report a critical service check status
@@ -398,9 +424,9 @@ module Datadog
398
424
  # it will be grouped with other events that don't have an event type.
399
425
  #
400
426
  # @param [String] title Event title
401
- # @param [String] text Event text. Supports \n
427
+ # @param [String] text Event text. Supports newlines (+\n+)
402
428
  # @param [Hash] opts the additional data about the event
403
- # @option opts [Integer, nil] :date_happened (nil) Assign a timestamp to the event. Default is now when none
429
+ # @option opts [Integer, String, nil] :date_happened (nil) Assign a timestamp to the event. Default is now when none
404
430
  # @option opts [String, nil] :hostname (nil) Assign a hostname to the event.
405
431
  # @option opts [String, nil] :aggregation_key (nil) Assign an aggregation key to the event, to group it with some others
406
432
  # @option opts [String, nil] :priority ('normal') Can be "normal" or "low"
@@ -460,7 +486,11 @@ module Datadog
460
486
  escaped_message = escape_service_check_message(message)
461
487
  sc_string << "|m:#{escaped_message}"
462
488
  else
463
- value = remove_pipes(opts[key])
489
+ if key == :timestamp && opts[key].is_a?(Integer)
490
+ value = opts[key]
491
+ else
492
+ value = remove_pipes(opts[key])
493
+ end
464
494
  sc_string << "|#{shorthand_key}#{value}"
465
495
  end
466
496
  end
@@ -470,13 +500,20 @@ module Datadog
470
500
  def format_event(title, text, opts=EMPTY_OPTIONS)
471
501
  escaped_title = escape_event_content(title)
472
502
  escaped_text = escape_event_content(text)
473
- event_string_data = "_e{#{escaped_title.length},#{escaped_text.length}}:#{escaped_title}|#{escaped_text}".dup
503
+ event_string_data = "_e{#{escaped_title.bytesize},#{escaped_text.bytesize}}:#{escaped_title}|#{escaped_text}".dup
474
504
 
475
505
  # We construct the string to be sent by adding '|key:value' parts to it when needed
476
506
  # All pipes ('|') in the metadata are removed. Title and Text can keep theirs
477
507
  OPTS_KEYS.each do |key, shorthand_key|
478
508
  if key != :tags && opts[key]
479
- value = remove_pipes(opts[key])
509
+ # :date_happened is the only key where the value is an Integer
510
+ # To not break backwards compatibility, we still accept a String
511
+ if key == :date_happened && opts[key].is_a?(Integer)
512
+ value = opts[key]
513
+ # All other keys only have String values
514
+ else
515
+ value = remove_pipes(opts[key])
516
+ end
480
517
  event_string_data << "|#{shorthand_key}:#{value}"
481
518
  end
482
519
  end
@@ -486,12 +523,13 @@ module Datadog
486
523
  event_string_data << "|##{tags_string}"
487
524
  end
488
525
 
489
- raise "Event #{title} payload is too big (more that 8KB), event discarded" if event_string_data.length > MAX_EVENT_SIZE
526
+ raise "Event #{title} payload is too big (more that 8KB), event discarded" if event_string_data.bytesize > MAX_EVENT_SIZE
490
527
  event_string_data
491
528
  end
492
529
 
493
530
  def tags_as_string(opts)
494
531
  if tag_arr = opts[:tags]
532
+ tag_arr = tag_hash_to_array(tag_arr) if tag_arr.is_a? Hash
495
533
  tag_arr = tag_arr.map { |tag| escape_tag_content(tag) }
496
534
  tag_arr = tags + tag_arr # @tags are normalized when set, so not need to normalize them again
497
535
  else
@@ -500,6 +538,10 @@ module Datadog
500
538
  tag_arr.join(COMMA) unless tag_arr.empty?
501
539
  end
502
540
 
541
+ def tag_hash_to_array(tag_hash)
542
+ tag_hash.to_a.map {|pair| pair.compact.join(":")}
543
+ end
544
+
503
545
  def escape_event_content(msg)
504
546
  msg.gsub NEW_LINE, ESC_NEW_LINE
505
547
  end
@@ -519,8 +561,8 @@ module Datadog
519
561
  end
520
562
 
521
563
  def send_stats(stat, delta, type, opts=EMPTY_OPTIONS)
522
- sample_rate = opts[:sample_rate] || 1
523
- if sample_rate == 1 or rand < sample_rate
564
+ sample_rate = opts[:sample_rate] || @sample_rate || 1
565
+ if sample_rate == 1 or rand <= sample_rate
524
566
  full_stat = ''.dup
525
567
  full_stat << @prefix if @prefix
526
568
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dogstatsd-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.0
4
+ version: 4.4.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: 2019-04-04 00:00:00.000000000 Z
11
+ date: 2019-07-15 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A Ruby DogStastd client
14
14
  email: code@datadoghq.com
@@ -40,7 +40,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
42
  requirements: []
43
- rubygems_version: 3.0.2
43
+ rubyforge_project:
44
+ rubygems_version: 2.7.6
44
45
  signing_key:
45
46
  specification_version: 4
46
47
  summary: A Ruby DogStatsd client