dogstatsd-ruby 4.8.2 → 5.4.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.
@@ -5,8 +5,13 @@ require_relative 'statsd/version'
5
5
  require_relative 'statsd/telemetry'
6
6
  require_relative 'statsd/udp_connection'
7
7
  require_relative 'statsd/uds_connection'
8
- require_relative 'statsd/batch'
8
+ require_relative 'statsd/connection_cfg'
9
+ require_relative 'statsd/message_buffer'
9
10
  require_relative 'statsd/serialization'
11
+ require_relative 'statsd/sender'
12
+ require_relative 'statsd/single_thread_sender'
13
+ require_relative 'statsd/forwarder'
14
+ require_relative 'statsd/timer'
10
15
 
11
16
  # = Datadog::Statsd: A DogStatsd client (https://www.datadoghq.com)
12
17
  #
@@ -26,13 +31,23 @@ require_relative 'statsd/serialization'
26
31
  # statsd = Datadog::Statsd.new 'localhost', 8125, tags: 'tag1:true'
27
32
  module Datadog
28
33
  class Statsd
34
+ class Error < StandardError
35
+ end
36
+
29
37
  OK = 0
30
38
  WARNING = 1
31
39
  CRITICAL = 2
32
40
  UNKNOWN = 3
33
41
 
34
- DEFAULT_BUFFER_SIZE = 8 * 1_024
42
+ UDP_DEFAULT_BUFFER_SIZE = 1_432
43
+ UDS_DEFAULT_BUFFER_SIZE = 8_192
44
+ DEFAULT_BUFFER_POOL_SIZE = Float::INFINITY
45
+
46
+ UDP_DEFAULT_SENDER_QUEUE_SIZE = 2048
47
+ UDS_DEFAULT_SENDER_QUEUE_SIZE = 512
48
+
35
49
  MAX_EVENT_SIZE = 8 * 1_024
50
+
36
51
  # minimum flush interval for the telemetry in seconds
37
52
  DEFAULT_TELEMETRY_FLUSH_INTERVAL = 10
38
53
 
@@ -51,77 +66,85 @@ module Datadog
51
66
  serializer.global_tags
52
67
  end
53
68
 
54
- # Buffer containing the statsd message before they are sent in batch
55
- attr_reader :buffer
56
-
57
- # Maximum buffer size in bytes before it is flushed
58
- attr_reader :max_buffer_bytes
59
-
60
69
  # Default sample rate
61
70
  attr_reader :sample_rate
62
71
 
63
- # Connection
64
- attr_reader :connection
65
-
66
72
  # @param [String] host your statsd host
67
73
  # @param [Integer] port your statsd port
68
74
  # @option [String] namespace set a namespace to be prepended to every metric name
69
75
  # @option [Array<String>|Hash] tags tags to be added to every metric
70
76
  # @option [Logger] logger for debugging
71
- # @option [Integer] max_buffer_bytes max bytes to buffer when using #batch
77
+ # @option [Integer] buffer_max_payload_size max bytes to buffer
78
+ # @option [Integer] buffer_max_pool_size max messages to buffer
79
+ # @option [Integer] sender_queue_size size of the sender queue in number of buffers (multi-thread only)
80
+ # @option [Numeric] buffer_flush_interval interval in second to flush buffer
72
81
  # @option [String] socket_path unix socket path
73
82
  # @option [Float] default sample rate if not overridden
83
+ # @option [Boolean] single_thread flushes the metrics on the main thread instead of in a companion thread
74
84
  def initialize(
75
85
  host = nil,
76
86
  port = nil,
87
+ socket_path: nil,
88
+
77
89
  namespace: nil,
78
90
  tags: nil,
79
- max_buffer_bytes: DEFAULT_BUFFER_SIZE,
80
- socket_path: nil,
81
- logger: nil,
82
91
  sample_rate: nil,
83
- disable_telemetry: false,
92
+
93
+ buffer_max_payload_size: nil,
94
+ buffer_max_pool_size: nil,
95
+ buffer_overflowing_stategy: :drop,
96
+ buffer_flush_interval: nil,
97
+
98
+ sender_queue_size: nil,
99
+
100
+ logger: nil,
101
+
102
+ single_thread: false,
103
+
104
+ telemetry_enable: true,
84
105
  telemetry_flush_interval: DEFAULT_TELEMETRY_FLUSH_INTERVAL
85
106
  )
86
107
  unless tags.nil? || tags.is_a?(Array) || tags.is_a?(Hash)
87
- raise ArgumentError, 'tags must be a Array<String> or a Hash'
108
+ raise ArgumentError, 'tags must be an array of string tags or a Hash'
88
109
  end
89
110
 
90
111
  @namespace = namespace
91
112
  @prefix = @namespace ? "#{@namespace}.".freeze : nil
92
-
93
113
  @serializer = Serialization::Serializer.new(prefix: @prefix, global_tags: tags)
114
+ @sample_rate = sample_rate
94
115
 
95
- transport_type = socket_path.nil? ? :udp : :uds
116
+ @forwarder = Forwarder.new(
117
+ connection_cfg: ConnectionCfg.new(
118
+ host: host,
119
+ port: port,
120
+ socket_path: socket_path,
121
+ ),
96
122
 
97
- @telemetry = Telemetry.new(disable_telemetry, telemetry_flush_interval,
98
123
  global_tags: tags,
99
- transport_type: transport_type
100
- )
124
+ logger: logger,
101
125
 
102
- @connection = case transport_type
103
- when :udp
104
- UDPConnection.new(host, port, logger, telemetry)
105
- when :uds
106
- UDSConnection.new(socket_path, logger, telemetry)
107
- end
126
+ single_thread: single_thread,
108
127
 
109
- @logger = logger
128
+ buffer_max_payload_size: buffer_max_payload_size,
129
+ buffer_max_pool_size: buffer_max_pool_size,
130
+ buffer_overflowing_stategy: buffer_overflowing_stategy,
131
+ buffer_flush_interval: buffer_flush_interval,
110
132
 
111
- @sample_rate = sample_rate
133
+ sender_queue_size: sender_queue_size,
112
134
 
113
- # we reduce max_buffer_bytes by a the rough estimate of the telemetry payload
114
- @batch = Batch.new(connection, (max_buffer_bytes - telemetry.estimate_max_size))
135
+ telemetry_flush_interval: telemetry_enable ? telemetry_flush_interval : nil,
136
+ )
115
137
  end
116
138
 
117
139
  # yield a new instance to a block and close it when done
118
140
  # for short-term use-cases that don't want to close the socket manually
119
- def self.open(*args)
120
- instance = new(*args)
141
+ # TODO: replace with ... once we are on ruby 2.7
142
+ def self.open(*args, **kwargs)
143
+ instance = new(*args, **kwargs)
121
144
 
122
145
  yield instance
123
146
  ensure
124
- instance.close
147
+ instance.close if instance
125
148
  end
126
149
 
127
150
  # Sends an increment (count = 1) for the given stat to the statsd server.
@@ -129,6 +152,7 @@ module Datadog
129
152
  # @param [String] stat stat name
130
153
  # @param [Hash] opts the options to create the metric with
131
154
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
155
+ # @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
132
156
  # @option opts [Array<String>] :tags An array of tags
133
157
  # @option opts [Numeric] :by increment value, default 1
134
158
  # @see #count
@@ -143,6 +167,7 @@ module Datadog
143
167
  # @param [String] stat stat name
144
168
  # @param [Hash] opts the options to create the metric with
145
169
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
170
+ # @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
146
171
  # @option opts [Array<String>] :tags An array of tags
147
172
  # @option opts [Numeric] :by decrement value, default 1
148
173
  # @see #count
@@ -158,13 +183,14 @@ module Datadog
158
183
  # @param [Integer] count count
159
184
  # @param [Hash] opts the options to create the metric with
160
185
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
186
+ # @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
161
187
  # @option opts [Array<String>] :tags An array of tags
162
188
  def count(stat, count, opts = EMPTY_OPTIONS)
163
189
  opts = { sample_rate: opts } if opts.is_a?(Numeric)
164
190
  send_stats(stat, count, COUNTER_TYPE, opts)
165
191
  end
166
192
 
167
- # Sends an arbitary gauge value for the given stat to the statsd server.
193
+ # Sends an arbitrary gauge value for the given stat to the statsd server.
168
194
  #
169
195
  # This is useful for recording things like available disk space,
170
196
  # memory usage, and the like, which have different semantics than
@@ -174,6 +200,7 @@ module Datadog
174
200
  # @param [Numeric] value gauge value.
175
201
  # @param [Hash] opts the options to create the metric with
176
202
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
203
+ # @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
177
204
  # @option opts [Array<String>] :tags An array of tags
178
205
  # @example Report the current user count:
179
206
  # $statsd.gauge('user.count', User.count)
@@ -188,6 +215,7 @@ module Datadog
188
215
  # @param [Numeric] value histogram value.
189
216
  # @param [Hash] opts the options to create the metric with
190
217
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
218
+ # @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
191
219
  # @option opts [Array<String>] :tags An array of tags
192
220
  # @example Report the current user count:
193
221
  # $statsd.histogram('user.count', User.count)
@@ -201,6 +229,7 @@ module Datadog
201
229
  # @param [Numeric] value distribution value.
202
230
  # @param [Hash] opts the options to create the metric with
203
231
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
232
+ # @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
204
233
  # @option opts [Array<String>] :tags An array of tags
205
234
  # @example Report the current user count:
206
235
  # $statsd.distribution('user.count', User.count)
@@ -217,6 +246,7 @@ module Datadog
217
246
  # @param [Integer] ms timing in milliseconds
218
247
  # @param [Hash] opts the options to create the metric with
219
248
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
249
+ # @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
220
250
  # @option opts [Array<String>] :tags An array of tags
221
251
  def timing(stat, ms, opts = EMPTY_OPTIONS)
222
252
  opts = { sample_rate: opts } if opts.is_a?(Numeric)
@@ -231,6 +261,7 @@ module Datadog
231
261
  # @param [String] stat stat name
232
262
  # @param [Hash] opts the options to create the metric with
233
263
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
264
+ # @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
234
265
  # @option opts [Array<String>] :tags An array of tags
235
266
  # @yield The operation to be timed
236
267
  # @see #timing
@@ -250,6 +281,7 @@ module Datadog
250
281
  # @param [Numeric] value set value.
251
282
  # @param [Hash] opts the options to create the metric with
252
283
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
284
+ # @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
253
285
  # @option opts [Array<String>] :tags An array of tags
254
286
  # @example Record a unique visitory by id:
255
287
  # $statsd.set('visitors.uniques', User.id)
@@ -270,9 +302,9 @@ module Datadog
270
302
  # @example Report a critical service check status
271
303
  # $statsd.service_check('my.service.check', Statsd::CRITICAL, :tags=>['urgent'])
272
304
  def service_check(name, status, opts = EMPTY_OPTIONS)
273
- telemetry.sent(service_checks: 1)
305
+ telemetry.sent(service_checks: 1) if telemetry
274
306
 
275
- send_stat(serializer.to_service_check(name, status, opts))
307
+ forwarder.send_message(serializer.to_service_check(name, status, opts))
276
308
  end
277
309
 
278
310
  # This end point allows you to post events to the stream. You can tag them, set priority and even aggregate them with other events.
@@ -290,17 +322,25 @@ module Datadog
290
322
  # @option opts [String, nil] :priority ('normal') Can be "normal" or "low"
291
323
  # @option opts [String, nil] :source_type_name (nil) Assign a source type to the event
292
324
  # @option opts [String, nil] :alert_type ('info') Can be "error", "warning", "info" or "success".
325
+ # @option opts [Boolean, false] :truncate_if_too_long (false) Truncate the event if it is too long
293
326
  # @option opts [Array<String>] :tags tags to be added to every metric
294
327
  # @example Report an awful event:
295
328
  # $statsd.event('Something terrible happened', 'The end is near if we do nothing', :alert_type=>'warning', :tags=>['end_of_times','urgent'])
296
329
  def event(title, text, opts = EMPTY_OPTIONS)
297
- telemetry.sent(events: 1)
330
+ telemetry.sent(events: 1) if telemetry
298
331
 
299
- send_stat(serializer.to_event(title, text, opts))
332
+ forwarder.send_message(serializer.to_event(title, text, opts))
300
333
  end
301
334
 
302
- # Send several metrics in the same UDP Packet
303
- # They will be buffered and flushed when the block finishes
335
+ # Send several metrics in the same packet.
336
+ # They will be buffered and flushed when the block finishes.
337
+ #
338
+ # This method exists for compatibility with v4.x versions, it is not needed
339
+ # anymore since the batching is now automatically done internally.
340
+ # It also means that an automatic flush could occur if the buffer is filled
341
+ # during the execution of the batch block.
342
+ #
343
+ # This method is DEPRECATED and will be removed in future v6.x API.
304
344
  #
305
345
  # @example Send several metrics in one packet:
306
346
  # $statsd.batch do |s|
@@ -308,50 +348,66 @@ module Datadog
308
348
  # s.increment('page.views')
309
349
  # end
310
350
  def batch
311
- @batch.open do
312
- yield self
313
- end
351
+ yield self
352
+ flush(sync: true)
314
353
  end
315
354
 
316
355
  # Close the underlying socket
317
- def close
318
- connection.close
356
+ #
357
+ # @param [Boolean, true] flush Should we flush the metrics before closing
358
+ def close(flush: true)
359
+ flush(sync: true) if flush
360
+ forwarder.close
361
+ end
362
+
363
+ def sync_with_outbound_io
364
+ forwarder.sync_with_outbound_io
365
+ end
366
+
367
+ # Flush the buffer into the connection
368
+ def flush(flush_telemetry: false, sync: false)
369
+ forwarder.flush(flush_telemetry: flush_telemetry, sync: sync)
370
+ end
371
+
372
+ def telemetry
373
+ forwarder.telemetry
374
+ end
375
+
376
+ def host
377
+ forwarder.host
378
+ end
379
+
380
+ def port
381
+ forwarder.port
382
+ end
383
+
384
+ def socket_path
385
+ forwarder.socket_path
386
+ end
387
+
388
+ def transport_type
389
+ forwarder.transport_type
319
390
  end
320
391
 
321
392
  private
322
393
  attr_reader :serializer
323
- attr_reader :telemetry
394
+ attr_reader :forwarder
324
395
 
325
- PROCESS_TIME_SUPPORTED = (RUBY_VERSION >= '2.1.0')
326
396
  EMPTY_OPTIONS = {}.freeze
327
397
 
328
- if PROCESS_TIME_SUPPORTED
329
- def now
330
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
331
- end
332
- else
333
- def now
334
- Time.now.to_f
335
- end
398
+ def now
399
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
336
400
  end
337
401
 
338
402
  def send_stats(stat, delta, type, opts = EMPTY_OPTIONS)
339
- telemetry.sent(metrics: 1)
403
+ telemetry.sent(metrics: 1) if telemetry
340
404
 
341
405
  sample_rate = opts[:sample_rate] || @sample_rate || 1
342
406
 
343
- if sample_rate == 1 || rand <= sample_rate
407
+ if sample_rate == 1 || opts[:pre_sampled] || rand <= sample_rate
344
408
  full_stat = serializer.to_stat(stat, delta, type, tags: opts[:tags], sample_rate: sample_rate)
345
409
 
346
- send_stat(full_stat)
347
- end
348
- end
349
-
350
- def send_stat(message)
351
- if @batch.open?
352
- @batch.add(message)
353
- else
354
- @connection.write(message)
410
+ forwarder.send_message(full_stat)
355
411
  end
356
412
  end
357
413
  end
metadata CHANGED
@@ -1,16 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dogstatsd-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.8.2
4
+ version: 5.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rein Henrichs
8
- autorequire:
8
+ - Karim Bogtob
9
+ autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2020-11-16 00:00:00.000000000 Z
12
+ date: 2022-03-01 00:00:00.000000000 Z
12
13
  dependencies: []
13
- description: A Ruby DogStastd client
14
+ description: A Ruby DogStatsd client
14
15
  email: code@datadoghq.com
15
16
  executables: []
16
17
  extensions: []
@@ -21,15 +22,20 @@ files:
21
22
  - LICENSE.txt
22
23
  - README.md
23
24
  - lib/datadog/statsd.rb
24
- - lib/datadog/statsd/batch.rb
25
25
  - lib/datadog/statsd/connection.rb
26
+ - lib/datadog/statsd/connection_cfg.rb
27
+ - lib/datadog/statsd/forwarder.rb
28
+ - lib/datadog/statsd/message_buffer.rb
29
+ - lib/datadog/statsd/sender.rb
26
30
  - lib/datadog/statsd/serialization.rb
27
31
  - lib/datadog/statsd/serialization/event_serializer.rb
28
32
  - lib/datadog/statsd/serialization/serializer.rb
29
33
  - lib/datadog/statsd/serialization/service_check_serializer.rb
30
34
  - lib/datadog/statsd/serialization/stat_serializer.rb
31
35
  - lib/datadog/statsd/serialization/tag_serializer.rb
36
+ - lib/datadog/statsd/single_thread_sender.rb
32
37
  - lib/datadog/statsd/telemetry.rb
38
+ - lib/datadog/statsd/timer.rb
33
39
  - lib/datadog/statsd/udp_connection.rb
34
40
  - lib/datadog/statsd/uds_connection.rb
35
41
  - lib/datadog/statsd/version.rb
@@ -38,10 +44,14 @@ licenses:
38
44
  - MIT
39
45
  metadata:
40
46
  bug_tracker_uri: https://github.com/DataDog/dogstatsd-ruby/issues
41
- changelog_uri: https://github.com/DataDog/dogstatsd-ruby/blob/v4.8.2/CHANGELOG.md
42
- documentation_uri: https://www.rubydoc.info/gems/dogstatsd-ruby/4.8.2
43
- source_code_uri: https://github.com/DataDog/dogstatsd-ruby/tree/v4.8.2
44
- post_install_message:
47
+ changelog_uri: https://github.com/DataDog/dogstatsd-ruby/blob/v5.4.0/CHANGELOG.md
48
+ documentation_uri: https://www.rubydoc.info/gems/dogstatsd-ruby/5.4.0
49
+ source_code_uri: https://github.com/DataDog/dogstatsd-ruby/tree/v5.4.0
50
+ post_install_message: |2+
51
+
52
+ If you are upgrading from v4.x of the dogstatsd-ruby library, note the major change to the threading model:
53
+ https://github.com/DataDog/dogstatsd-ruby#migrating-from-v4x-to-v5x
54
+
45
55
  rdoc_options: []
46
56
  require_paths:
47
57
  - lib
@@ -49,16 +59,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
49
59
  requirements:
50
60
  - - ">="
51
61
  - !ruby/object:Gem::Version
52
- version: 2.0.0
62
+ version: 2.1.0
53
63
  required_rubygems_version: !ruby/object:Gem::Requirement
54
64
  requirements:
55
65
  - - ">="
56
66
  - !ruby/object:Gem::Version
57
67
  version: '0'
58
68
  requirements: []
59
- rubyforge_project:
60
- rubygems_version: 2.7.10
61
- signing_key:
69
+ rubygems_version: 3.2.22
70
+ signing_key:
62
71
  specification_version: 4
63
72
  summary: A Ruby DogStatsd client
64
73
  test_files: []
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Datadog
4
- class Statsd
5
- class Batch
6
- def initialize(connection, max_buffer_bytes)
7
- @connection = connection
8
- @max_buffer_bytes = max_buffer_bytes
9
- @depth = 0
10
- reset
11
- end
12
-
13
- def open
14
- @depth += 1
15
-
16
- yield
17
- ensure
18
- @depth -= 1
19
- flush if !open?
20
- end
21
-
22
- def open?
23
- @depth > 0
24
- end
25
-
26
- def add(message)
27
- message_bytes = message.bytesize
28
-
29
- unless @buffer_bytes == 0
30
- if @buffer_bytes + 1 + message_bytes >= @max_buffer_bytes
31
- flush
32
- else
33
- @buffer << "\n"
34
- @buffer_bytes += 1
35
- end
36
- end
37
-
38
- @buffer << message
39
- @buffer_bytes += message_bytes
40
- end
41
-
42
- def flush
43
- return if @buffer_bytes == 0
44
- @connection.write(@buffer)
45
- reset
46
- end
47
-
48
- private
49
-
50
- def reset
51
- @buffer = String.new
52
- @buffer_bytes = 0
53
- end
54
- end
55
- end
56
- end